字符型常量和字符串常量有什么区别?
- 形式上:字符常量是单引号
'
引起的一个字符,如'A','1'
;而字符串常量是双引号"
引起的 0 个或若干个字符,如"","A","1"
- 含义上:字符常量相当于一个整型值(
ASCII
值),可以参加表达式运算;而字符串常量代表堆内存中的一个地址; - 存储空间上:字符常量只占 2 个字节;而字符串常量占若干个字节
字符包装类
Character
中成员变量Character.SIZE
值为 16,单位是bits
,该值除以 8 后就得到 2 个字节(1 byte = 8 bits
)
Java 中有哪些常见的关键字?
用途 | 关键字 |
---|---|
访问控制 | private、protected、public |
类、方法和变量修饰符 | abstract、final、class、interface、extends、implement、native、enum、new、static、strictfp、synchronized、transient、volatile |
流程控制 | break、continue、while、do、for、if、else、instanceof、switch、case、default |
包相关 | import、package |
基本类型 | boolean、byte、short、int、long、char、float、double |
变量引用 | super、this、void |
保留字 | goto、const |
自增和自减运算符
自增运算符指 ++
,自减运算符 --
。这两种运算符都可以放在变量前和变量后,当运算符放在变量前时表示:先运算再赋值即先自增/减,再赋值;反之则表示先赋值再运算。如 b = ++a
表示 a
先自增,再赋值给 b
;b = a++
表示先将 a
赋值给 b
后,a
再自增。
== 和 equals() 的区别
- 对于基本类型:
==
比较的是两者的值是否相等 - 对于引用类型:
==
比较的是两个对象是否指向堆内存中的同一个对象地址 - 对于引用类型:如果没有重写
equals()
方法时,则和引用类型的==
比较逻辑一致;如果重写了equals()
方法,则比较两个对象中内容是否相同装箱和拆箱
装箱和拆箱的定义
Java 为每种基本都创建了对应的包装类型。在Java SE5
以前,如果生成一个数值为 10 的Integer
对象,必须这样写:
而自从Integer i = new Integer(10);
Java SE5
开始提供自动装箱特性后,再生成一个数值为 10 的Integer
对象,只需要:
这个过程中会自动根据数组创建对应的Integer i = 10;
Integer
对象,这就是装箱。简单说,装箱就是将基本数据类型转换为包装类型的过程;而拆箱则相反,将包装类型转换为基本类型的过程称为拆箱,如下:int i = 10;
装箱和拆箱实现原理
以 Integer 类为例编写如下代码:
反编译 class 文件后得到如下内容:public class Main {
public static void main(String[] args){
Integer i = 0;
int n = 0;
}
}
观察字节码文件内容可以发现,自动装箱是通过调用Integer
的valueOf()
方法实现;而自动拆箱则是调用Integer
的intValue()
方法实现;double、char 等其他的基本类型也基本一样
由此得出结论:装箱过程是通过调用包装类型的**valueOf()**
方法实现,而拆箱过程是通过包装类型的**xxxValue()**
方法实现(**xxx**
代表对应的基本类型)装箱拆箱面试常见问题
如下代码的输出结果是什么
public class Main {
public static void main(String[] args) {
Integer i1 = 100;
Integer i2 = 100;
Integer i3 = 200;
Integer i4 = 200;
System.out.println(i1==i2); // true
System.out.println(i3==i4); // false
}
}
原因是因为
Integer
包装类型的valueOf()
方法会将[-127,128]
常用数值区间内的整数缓存起来。在创建时如果数值在该区间,则从缓存中取现有对象的引用,反之则创建新对象如下代码输出结果是什么
public class Main {
public static void main(String[] args) {
Double i1 = 100.0;
Double i2 = 100.0;
Double i3 = 200.0;
Double i4 = 200.0;
System.out.println(i1==i2); // false
System.out.println(i3==i4); // false
}
}
原因是因为
Double
包装类型的valueOf()
方法并没有Integer
类的valueOf()
方法缓存逻辑,创建的都是新对象如下代码输出结果是什么
public class Main {
public static void main(String[] args) {
Boolean i1 = false;
Boolean i2 = false;
Boolean i3 = true;
Boolean i4 = true;
System.out.println(i1==i2); // true
System.out.println(i3==i4); // true
}
}
原因是因为
Boolean
类型的valueOf()
方法内部实现逻辑public static Boolean valueOf(boolean b) {
return (b ? TRUE : FALSE);
}
其中
TRUE
和FALSE
是Boolean
类中定义的 2 个静态成员变量如下代码输出结果是什么
public class Main {
public static void main(String[] args) {
Integer a = 1;
Integer b = 2;
Integer c = 3;
Integer d = 3;
Integer e = 321;
Integer f = 321;
Long g = 3L;
Long h = 2L;
System.out.println(c==d); // true
System.out.println(e==f); // false
System.out.println(c==(a+b)); // true
System.out.println(c.equals(a+b)); // true
System.out.println(g==(a+b)); // true
System.out.println(g.equals(a+b)); // false
System.out.println(g.equals(a+h)); // true
}
}
原因是因为包装类型包含了算术运算,因此会先触发自动拆箱过程,然后再比较它们的数值是否相等。倒数第二个是因为两个数值拆箱后的类型不同,所以结果为
false
,而最后一个是因为拆箱后进行加法运算时int
类型自动向上转型为long
后再比较数组,故结果为true
重载和重写的区别
重载
重载发生同一个类中(或者子类和父类直接),方法名相同,参数类型不同、个数不同、顺序不同,方法返回值和访问修饰符可以不同。按方法类型区分可以分为构造方法重载和普通方法重载
重载就是同一个类中多个同名方法根据不同的传参来执行不同的逻辑处理
重写
重写发生在运行期,是子类对父类的允许的访问的方法的实现过程进行重新编写
- 返回值类型、方法名、参数列表必须完全相同,抛出的异常范围小于等于父类,访问修饰符范围大于等于父类
- 如果父类方法访问修饰符为
private/final/static
则子类就不能重写该方法,但是被static
修饰的方法能够被再次声明 - 构造方法无法被重写
重写就是子类对父类方法的重新编写,外部样子不能改变,内部逻辑可以改变
深拷贝和浅拷贝
- 深拷贝:对基本数据类型进行值传递,而对引用数据类型则是创建一个新的对象,并复制其内容
- 浅拷贝:对基本数据类型进行值传递,而对引用数据类型则是进行引用传递般的拷贝,浅拷贝后的对象和原对象指向的是堆内存中的同一个地址