1. 8 种基本数据类型
- 整数类型
- byte :
它是最小的整数类型,占用 1 个字节(8 位)。在一些对内存使用要求极高的场景,比如嵌入式系统开发、数据传输时对数据量有严格限制的情况,会使用byte
类型。例如,在处理图像数据中的像素值时,如果每个像素值范围在 -128 到 127 之间,就可以用byte
存储。 - short :
占用 2 个字节(16 位)。在一些特定的图形处理库中,可能会使用short
来表示一些相对较小的坐标值或颜色分量值,因为它比int
更节省内存。 - int :
这是 Java 中最常用的整数类型,占用 4 个字节(32 位)。在日常的编程中,如计数、索引、循环控制等场景,通常都会使用int
类型。例如,遍历数组、计算循环次数等。 - long :
占用 8 个字节(64 位)。当需要处理非常大的整数时,如计算时间戳(以毫秒为单位)、处理大数据量的统计结果等,就需要使用long
类型。定义时,需要在数字后面加上L
或l
,建议使用大写的L
,因为小写的l
容易与数字 1 混淆。
- byte :
- 浮点类型
- float :
单精度浮点数,占用 4 个字节(32 位)。在一些对精度要求不高的科学计算、图形处理中的一些近似计算等场景中使用。例如,在游戏开发中,计算物体的位置、速度等,可能只需要一定的精度,此时可以使用float
类型。定义时需要在数字后面加上F
或f
。 - double :
双精度浮点数,占用 8 个字节(64 位)。是 Java 中默认的浮点类型,精度比float
高。在大多数需要处理小数的场景中,如金融计算、科学研究等,都会使用double
类型。
- float :
- 字符类型
- char :
占用 2 个字节(16 位),基于 Unicode 字符集,可以表示世界上大多数语言的字符。在处理文本、字符串操作时,char
类型非常有用。例如,遍历字符串中的每个字符时,就会用到char
类型。
- char :
- 布尔类型
- boolean :
只有两个值,true
和false
,用于逻辑判断。在条件语句(如if
、while
等)、逻辑运算中广泛使用。
- boolean :
2. 什么是装箱和拆箱
- 装箱
- 自动装箱:Java 编译器会自动将基本数据类型转换为对应的包装类对象。例如:
java
int num = 10;
Integer integerObj = num; // 自动装箱
2. 手动装箱 :在 Java 9 及以前,可以使用包装类的构造方法进行装箱;Java 9 及以后,部分构造方法已被弃用,建议使用 valueOf
方法。例如:
java
int num = 10;
// Java 9 以前
// Integer integerObj2 = new Integer(num);
// Java 9 及以后
Integer integerObj2 = Integer.valueOf(num);
使用 valueOf
方法的好处是,它会缓存一些常用的值,提高性能。例如,Integer
类会缓存 -128 到 127 之间的整数对象,当使用 valueOf
方法创建这个范围内的对象时,会直接返回缓存的对象,而不是创建新的对象。
- 拆箱
- 自动拆箱:Java 编译器会自动将包装类对象转换为对应的基本数据类型。例如:
java
Integer integer = 20;
int num2 = integer; // 自动拆箱
2. 手动拆箱 :调用包装类的 xxxValue()
方法进行拆箱。例如:
java
Integer integer = 20;
int num3 = integer.intValue(); // 手动拆箱
3. String 转出 int 型,判断能不能转?如何转?
- 判断能否转换的更多方法
- 正则表达式:可以使用正则表达式判断字符串是否只包含数字字符。例如:
java
import java.util.regex.Pattern;
public class StringToInt {
public static boolean isNumeric(String str) {
Pattern pattern = Pattern.compile("^-?\\d+$");
return pattern.matcher(str).matches();
}
public static void main(String[] args) {
String str = "123";
if (isNumeric(str)) {
try {
int num = Integer.parseInt(str);
System.out.println(num);
} catch (NumberFormatException e) {
System.out.println("字符串不能转换为整数");
}
} else {
System.out.println("字符串不是有效的数字");
}
}
}
2. 尝试转换并捕获异常 :直接尝试使用 Integer.parseInt()
或 Integer.valueOf()
方法进行转换,并捕获 NumberFormatException
异常。例如:
java
public class StringToInt2 {
public static void main(String[] args) {
String str = "abc";
try {
int num = Integer.parseInt(str);
System.out.println(num);
} catch (NumberFormatException e) {
System.out.println("字符串不能转换为整数");
}
}
}
- 转换方法的性能分析
Integer.parseInt()
方法直接返回基本数据类型int
,效率相对较高,适用于需要直接使用int
类型的场景。Integer.valueOf()
方法返回的是Integer
对象,在需要基本数据类型时会自动拆箱。如果需要使用Integer
对象,或者在泛型、集合等场景中使用,建议使用Integer.valueOf()
方法。
4. short s1 = 1; s1 = s1 + 1; 有什么错?short s1 = 1; s1 +=1; 有什么错?
short s1 = 1; s1 = s1 + 1;
在 Java 中,当进行算术运算时,short
、byte
和char
类型会自动提升为int
类型。所以s1 + 1
的结果是int
类型。而s1
是short
类型,将int
类型的值赋给short
类型的变量需要进行强制类型转换,否则会导致编译错误。正确的写法是:
java
short s1 = 1;
s1 = (short) (s1 + 1);
short s1 = 1; s1 +=1;
+=
是复合赋值运算符,它会自动进行类型转换。s1 += 1
实际上等价于s1 = (short)(s1 + 1)
,所以不会有编译错误。
5. Int 与 Integer 区别
- 内存使用差异
int
类型变量直接存储值,存储在栈内存中,占用的内存空间是固定的,根据类型不同而不同(如int
占用 4 个字节)。Integer
是引用类型,变量存储的是对象的引用,对象本身存储在堆内存中,引用存储在栈内存中。除了对象本身占用的内存外,还需要额外的内存来存储引用。
- 性能差异
- 在进行大量的数值计算时,
int
类型的性能要高于Integer
类型。因为int
类型不需要进行装箱和拆箱操作,而Integer
类型在进行计算时需要进行装箱和拆箱,会带来一定的性能开销。
6. 字节字符区别
- 编码与解码
- 字节:字节是计算机存储和传输数据的基本单位,在不同的编码格式下,字节与字符的对应关系不同。例如,在 ASCII 编码中,一个字节对应一个字符;在 UTF - 8 编码中,一个字符可能由 1 到 4 个字节表示。
- 字符 :字符是人类能够识别的符号,在 Java 中,
char
类型基于 Unicode 字符集。在进行文件读写、网络传输等操作时,需要进行字符编码和解码。例如,将字符串转换为字节数组时,需要指定编码格式:
java
String str = "你好";
try {
byte[] bytes = str.getBytes("UTF-8");
} catch (Exception e) {
e.printStackTrace();
}
- 处理场景
- 字节处理 :在处理二进制文件(如图片、视频、音频等)时,主要使用字节进行操作。例如,使用
FileInputStream
和FileOutputStream
进行文件读写时,读取和写入的都是字节数据。 - 字符处理 :在处理文本文件(如
.txt
文件)、进行字符串操作时,主要使用字符进行操作。例如,使用FileReader
和FileWriter
进行文件读写时,读取和写入的都是字符数据。
7. 基本类型与引用类型的区别
- 垃圾回收
- 基本类型:基本类型变量存储在栈内存中,当变量的作用域结束时,栈内存中的空间会自动释放,不需要垃圾回收机制进行处理。
- 引用类型:引用类型对象存储在堆内存中,当对象不再被引用时,会被垃圾回收机制标记为可回收对象,在合适的时机进行回收。
- 数组与基本类型、引用类型的关系
- 基本类型数组:数组中的每个元素都是基本类型,数组本身是引用类型,存储在堆内存中,而数组元素存储在数组所占用的内存空间中。例如:
java
int[] arr = new int[10];
2. 引用类型数组:数组中的每个元素都是引用类型,数组本身和数组元素都存储在堆内存中。例如:
java
String[] strArr = new String[10];
8. 重写重载封装继承多态
- 重写的注意事项
- 异常处理 :子类重写的方法不能抛出比父类被重写方法更多、更宽泛的异常。例如,如果父类方法抛出
IOException
,子类重写的方法可以抛出IOException
或其子类异常,或者不抛出异常。 - 方法签名严格匹配:方法名、参数列表和返回值类型必须与父类被重写的方法相同(子类返回值类型可以是父类返回值类型的子类,这称为协变返回类型)。
- 重载的实现原理
重载方法是根据方法的参数列表(参数个数、类型或顺序)来区分的。在编译时,编译器会根据调用方法时传递的参数类型和数量,选择合适的重载方法进行调用。
- 封装的优势
- 提高安全性 :通过将数据和操作数据的方法封装在一起,使用
private
访问修饰符隐藏对象的内部实现细节,只对外提供必要的接口,可以防止外部代码直接访问和修改对象的内部数据,从而提高代码的安全性。 - 提高可维护性:当需要修改对象的内部实现时,只需要修改封装类的内部代码,而不会影响到外部调用代码,降低了代码的耦合度,提高了代码的可维护性。
- 继承的限制和拓展
- 单继承限制:Java 只支持单继承,即一个子类只能有一个直接父类。但可以通过实现多个接口来实现类似多继承的功能。
- 方法重写和调用父类方法 :子类可以重写父类的方法来实现自己的功能,同时可以使用
super
关键字调用父类的方法。例如:
java
class Parent {
public void print() {
System.out.println("Parent");
}
}
class Child extends Parent {
@Override
public void print() {
super.print();
System.out.println("Child");
}
}
- 多态的实现方式和应用场景
- 实现方式:多态的实现方式有继承和接口。通过父类引用指向子类对象,在运行时根据实际对象类型调用相应的方法。例如:
java
class Animal {
public void sound() {
System.out.println("Animal makes a sound");
}
}
class Dog extends Animal {
@Override
public void sound() {
System.out.println("Dog barks");
}
}
public class Main {
public static void main(String[] args) {
Animal animal = new Dog();
animal.sound();
}
}
2.应用场景:多态在代码的可扩展性和可维护性方面有很大的优势。例如,在游戏开发中,不同的角色可能有不同的攻击方式,可以通过多态来实现统一的攻击接口,根据实际角色类型调用相应的攻击方法。
9.是否可以覆盖 (override) 一个 private 或者是 static 的方法?
- private 方法
private
方法的作用域仅限于当前类,子类无法访问父类的private
方法。所以子类中定义的与父类private
方法同名的方法,只是一个新的方法,与父类的方法没有关系。例如:
java
class Parent {
private void privateMethod() {
System.out.println("Parent private method");
}
}
class Child extends Parent {
public void privateMethod() {
System.out.println("Child private method");
}
}
在这个例子中,Child
类中的 privateMethod
方法与 Parent
类中的 privateMethod
方法没有重写关系。
- static 方法
静态方法属于类,而不是对象。子类可以定义与父类静态方法同名的静态方法,这称为方法隐藏。调用时,根据引用类型决定调用哪个方法,而不是根据实际对象类型。例如:
java
class Parent {
public static void staticMethod() {
System.out.println("Parent static method");
}
}
class Child extends Parent {
public static void staticMethod() {
System.out.println("Child static method");
}
}
public class Main {
public static void main(String[] args) {
Parent parent = new Child();
parent.staticMethod(); // 调用 Parent 类的 staticMethod 方法
}
}
10. 什么是 PriorityQueue
- 实现原理
PriorityQueue
基于优先级堆实现,优先级堆是一种完全二叉树,它的每个节点的值都大于或等于其子节点的值(最大堆),或者小于或等于其子节点的值(最小堆)。在 PriorityQueue
中,默认是最小堆。插入和删除元素的时间复杂度为 O (log n),获取队首元素的时间复杂度为 O (1)。
- 自定义比较器
可以通过提供自定义的 Comparator
来指定元素的排序规则。例如,要创建一个最大堆的 PriorityQueue
:
java
import java.util.PriorityQueue;
import java.util.Comparator;
public class PriorityQueueExample {
public static void main(String[] args) {
PriorityQueue<Integer> pq = new PriorityQueue<>(Comparator.reverseOrder());
pq.add(3);
pq.add(1);
pq.add(2);
while (!pq.isEmpty()) {
System.out.println(pq.poll());
}
}
}
- 应用场景拓展
- 任务调度 :在操作系统中,任务调度器可以使用
PriorityQueue
来管理任务,根据任务的优先级来决定任务的执行顺序。 - Dijkstra 算法 :在图算法中,Dijkstra 算法可以使用
PriorityQueue
来优化,提高算法的效率。
友情提示:本文已经整理成文档,可以到如下链接免积分下载阅读