文章预览
Java
基本数据类型
Java的数据类型分为两大类:
基本类型和引用类型
自动拆装箱、发生与实现?
每个基本数据类型都对应一个包装类,除了 int 和 char 对应 Integer 和 Character 外,其余基本数据类型的包装类都是首字母大写即可,比较两个包装类数值要用 equals ,而不能用 == 。
-
自动装箱:
将基本数据类型包装为一个包装类对象,例如向一个泛型为 Integer 的集合添加 int 元素。
-
使用基础类型给引用类型变量赋值;
-
具体实现:调用引用类型对应的静态方法valueOf,
本质是在该方法内部调用构造函数创建对象。
自动装箱的缓存机制:
为了节省内存和提升性能,Java给多个包装类提供了缓存机制,可以在自动装箱过程中,把一部分对象放到缓存中(引用常量池),实现了对象的复用。如Byte、Short、Integer、Long、Character等都支持缓存。
-
自动拆箱:
将一个包装类对象转换为一个基本数据类型,例如将一个包装类对象赋值给一个基本数据类型的变量。
-
当基础类型与引用类型进行 “==、+、-、×、÷” 运算时,会对引用类型进行自动拆箱;
-
具体实现,
引用类型对象内部包含对应基本类型的成员变量
,自动拆箱时返回该成员变量即可;
== 与 equals 区别
"=="对比的是栈中的内容:
-
基本数据类型比较的是变量值;
-
引用类型比较的堆中内存对象的地址。
equals 本质上就是 ==,只不过 String 和 Integer 等重写了 equals 方法,把它变成了值比较。
注:对于"=="中引用类型,除非是同一个new出来的对象,他们的比较后的结果为true,否则比较后结果为false。因为每new一次,都会重新开辟堆内存空间。
String中重写equals方法举例:
变成值的对比
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = value.length;
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {
if (v1[i] != v2[i]) //实质比较字符的ascii值,即两个字符串内容的比较!
return false;
i++;
}
return true;
}
}
return false;
}
应用案例分析:
String str1 = "Hello"; // 分配到常量池
String str2 = new String("Hello"); // 在堆中分配内存
String str3 = str2; // 引用传递
System.out.println(str1 == str2); // false,比较两者栈中的内存地址,显然不同
System.out.println(str1 == str3); // false
System.out.println(str2 == str3); // true
System.out.println(str1.equals(str2)); // true,string重写了equals方法,实际上对比的两个字符串的内容
System.out.println(str1.equals(str3)); // true
System.out.println(str2.equals(str3)); // true
注意:以后进行字符串/包装类相等判断的时候使用equals(),即对比内容。
int和Integer的区别
-
Integer是int的包装类,int则是java的一种基本数据类型
-
Integer变量必须实例化后才能使用,而int变量不需要
-
Integer实际是对象的引用,当new一个Integer时,实际上是生成一个指针指向此对象;而int则是直接存储数据值 。
-
Integer的默认值是null,int的默认值是0
Integer a= 127与 Integer b = 127相等吗?
对于对象引用类型:==比较的是对象的内存地址,对于基本数据类型:==比较的是值。
Integer内部有一个IntegerCache的内部类。
对整型值在-128到127之间的对象进行缓存。
缓存会在Integer类第一次使用的时候被初始化出来,那么
自动装箱时不会new新的Integer对象,而是直接引用常量池中的Integer对象(直接从缓存中取出)
,超过范围 a1==b1的结果是false。
注:若数值超过缓存缓存范围,则需要创建新的对象。
public static void main(String[] args) {
Integer a = new Integer(1);
Integer b = 1; // 将1自动装箱成Integer类型
int c = 1;
System.out.println(a == b); // false,引用了不同对象
System.out.println(a == c); // true,a自动拆箱与c比较
System.out.println(b == c); // true
Integer a1 = 128;
Integer b1 = 128;
System.out.println(a1 == b1); // false, 自动装箱,new新的Integer对象
Integer a2 = 127;
Integer b2 = 127;
System.out.println(a2 == b2); // true,字面值介于-128与127, 自动装箱不会new新的Integer对象,直接引用常量池中的对象
}
java有了基本类型,为什么还要设计对应的引用类型?
-
可以使用为该引用类型而编写的方法
-
Java集合(map、set、list)等所有集合只能存放引用类型数据,不能存放基本类型数据(
容器中实际存放的是对象的引用
)。
-
引用类型对象存储在堆上,可以控制其生命周期;而基本类型存储在栈上,会随着代码块运行结束被回收
-
java泛型使用类型擦除法实现,基本类型无法实现泛型。
java泛型相关问题
泛型
:把
类型明确的工作
推迟到创建对象或调用方法的时候才去进行。本质就是
将类型参数化,解决不确实对象具体类型的问题。
好处
:
-
类型安全,放置什么出来就是什么,不存在 ClassCastException。
-
提升可读性,编码阶段就显式知道泛型集合、泛型方法等处理的对象类型。
-
代码重用,合并了同类型的处理代码。
泛型擦除是什么?介绍下常用的通配符
原理:
泛型只存在于编译阶段,不存在与运行阶段
(编译后的class文件不存在泛型的概念)。例如定义 List<Object> 或 List<String>,在编译后都会变成 List 。
定义一个泛型类型,会自动提供一个对应原始类型,类型变量会被擦除。如果没有限定类型就会替换为 Object,如果有限定类型就会替换为第一个限定类型,例如 <T extends A & B> 会使用 A 类型替换 T。
常用的通配符为: T,E,K,V,?
(1)? 表示不确定的 java 类型
(2)T (type) 表示具体的一个 java 类型
(3)K V (key value) 分别代表 java 键值中的 Key Value
(4)E (element) 代表 Element
什么是字符串常量池(String Pool)?
-
字符串常量池位于堆内存中,专门用来存储字符串常量,可以提高内存的使用率,避免开辟多块空间存储相同的字符串;
-
在创建字符串时 JVM 会首先检查字符串常量池,如果该字符串已经存在池中,则返回它的引用,如果不存在,则
实例化一个字符串通过 String 的 intern() 方法放到池中,并返回其引用
。
String a = "a" + new String("b")创建了几个对象?
-
常量和常量拼接仍是常量,结果在常量池,只要有变量参与拼接结果就是变量,存在堆。
-
使用字面量时只创建一个常量池中的常量,使用 new 时如果常量池中没有该值就会在常量池中新创建,再在堆中创建一个对象,引用常量池中常量。因此 String a = "a" + new String("b") 会创建四个对象,常量池中的 a 和 b,堆中的 b 和堆中的 ab。
String(好处)、StringBuffer与StringBuilder的区别和应用场景
-
String是final修饰的,不可变,每次操作都会产生新的String对象
(一定程度上导致了内存浪费)。
-
StringBuffer和StringBuilder都是在原对象上进行操作
(不会产生新对象)。
注:String的好处如下:
-
可以缓存 hash 值
:因为 String 的 hash 值经常被使用,例如 String 用做 HashMap 的 key。不可变的特性可以使得 hash 值也不可变,因此只需要进行一次计算。
-
String Pool
的需要
:如果一个 String 对象已经被创建过了,那么就会从 String Pool 中取得引用。只有 String 是不可变的,才可能使用 String Pool。
-
安全性
:String 经常作为参数,String 不可变性可以保证参数不可变。例如在作为网络连接参数的情况下如果 String 是可变的,那么在网络连接过程中,String 被改变,改变 String 的那一方以为现在连接的是其它主机,而实际情况却不一定是。
三者区别(线程安全性与性能):
-
线程安全性
:StringBuffer是线程安全的(内部方法都用synchronized关键字修饰),StringBuilder是线程不安全的;String不可变性保证线程安全,
-
性能(效率)
:StringBuilder>StringBuffer>String;
注:这里谈到一个对象是否线程安全,是不是需要额外进行加锁,保证满足三个条件:多线程环境下、变量为共享变量和结果不受影响。
应用场景
:操作少量数据使用String;StringBuffer和StringBuilder经常需要改变字符串内容时使用:单线程操作字符串缓冲区下⼤量数据使用StringBuilder;多线程(共享变量)操作字符串缓冲区下⼤量数据为了保证结果的正确性使用StringBuffer。
关于 final 关键字的⼀些总结
final 关键字主要应用位置:变量、方法和类
-
对于⼀个 final 变量,如果是基本数据类型的变量,则其数值⼀旦在初始化之后便不能更改;如果是引⽤类型的变量,则在对其初始化之后便不能再让其指向另⼀个对象。
-
当⽤ final 修饰⼀个类时,表明这个类不能被继承。final 类中的所有成员⽅法都会被隐式地指定为 final ⽅法。
-
使⽤ final ⽅法,目的是把⽅法锁定,以防任何继承类修改它的含义,也就是说final方法不能被重写。
final finally
finalize区别
-
final可以修饰类、变量、方法,修饰类表示该类不能被继承、修饰方法表示该方法不能被重写、修饰变量表示该变量是一个常量不能被重新赋值(引用类型变量初始化后不能在指向另一个对象)。
-
finally一般作用在try-catch代码块
中,在
处理异常
的时候,通常我们将
一定要执行的代码方法finally代码块
中,表示不管是否出现异常,该代码块都会执行,一般用来存放一些关闭资源的代码。
-
finalize是一个方法,属于Object类的一个方法,而Object类是所有类的父类,该方法一般由垃圾回收器来调用,当我们调用System.gc() 方法的时候,由垃圾回收器调用finalize(),回收垃圾,一个对象是否可回收的最后判断。
finally语句块什么情况不会执行
-
finally语句块第一行出现异常
-
程序抛出异常(或者return)之前,调用system.exits(int),退出程序
-
程序抛出异常(或者return)之前,线程死亡
-
程序抛出异常(或者return)之前,关闭CPU
注意: 当 try 语句和 finally 语句中都有 return 语句时,在方法返回之前,finally 语句的内容将被执行,并且 finally 语句的返回值将会覆盖原始的返回值。
java异常常见问题
异常是什么:
Throwable
派生出
Error
、
Exception
。Exception又分为
运行时异常
和
非运行时异常。
-
error:
程序(JVM)无法处理的严重错误 ,不能通过 catch 来进行捕获,通常会导致程序终止。例如系统崩溃、内存不足、虚拟机运行错误、类定义错误等;
-
exception:
程序可以处理的异常,需要对其进行处理 ;
Exception分类
:
-
运行时异常(非受检异常):
继承自
RuntimeException
。RuntimeException及其子类都统称为非受检查异常,例如:NullPointExecrption、NumberFormatException(字符串转换为数字)、ArrayIndexOutOfBoundsException(数组越界)、ClassCastException(类型转换错误)、ArithmeticException(算术错误)等。可以由程序员自己决定(因为运行时异常中有很多是代码本身写错了,需要的不是处理异常,而是修改代码,如空指针异常、数据访问越界异常)。
-
非运行时异常(受检异常):
必须对其进行处理,需要用 try...catch... 语句捕获并进行处理,并且可以从异常中恢复。除了RuntimeException及其子类以外,其他的Exception类及其子类都属于受检异常。常见的受检查异常有:IO 相关的异常、ClassNotFoundException、SQLException
java常见的异常
:。
-
java.lang.InstantiationError:
实例化错误
。当一个应用试图通过new操作符构造一个抽象类或者接口时抛出该异常.
-
java.lang.OutOfMemoryError:
内存不足错误
。当可用内存不足以让Java虚拟机分配给一个对象时抛出该错误。
-
java.lang.StackOverflowError:
堆栈溢出错误
。当一个应用递归调用的层次太深而导致堆栈溢出或者陷入死循环时抛出该错误。
-
java.lang.IndexOutOfBoundsException:
索引越界异常
。当访问某个序列的索引值小于0或大于等于序列大小时,抛出该异常。
-
java.lang.NullPointerException:
空指针异常
。
-
java.lang.ArithmeticException:
算术条件异常
。譬如:整数除零等。
Throwable
类常用方法
:
-
public string
getMessage():
返回异常发生时的简要描述;
-
public string
toString():
返回异常发生时的详细信息;
-
public string
getLocalizedMessage():
返回异常对象的本地化信息。使用Throwable 的子类覆盖这个方法,可以生成本地化信息。如果子类没有覆盖该方法,则该方法返回的信息与getMessage()返回的结果相同;
-
public void
printStackTrace():
在控制台上打印 Throwable 对象封装的异常信息。
………………………………