Java 基础面试题
[toc]
面向对象编程有哪些特征?
继承
是一种代码重用的机制,允许一个类继承另外一个类的成员和方法,使得子类也能具有父类的相同行为;
java 类之间只能实现单继承,接口之间可以多继承
多态
父类引用指向不同的实现对象,编译看左边,运行看右边
抽象
抽象是对同一个目标的共有属性、特征、方法、功能、行为等的描述和总结、关注对象的行为而非具体的实现
抽象主要通过抽象类和接口实现,抽象类是不能被实例化的,它至少含有一个抽象方法,可以有已经实现的方法;接口则是一种特殊的抽象类,一般仅包含抽象方法的声明,java8开始支持接口的默认方法和静态方法实现
封装
隐藏对象的属性和细节,说人话就是对象内部的方法或者属性使用 private 来修饰,不让外部直接访问。对象只提供 public 方法来间接的方法 private 修饰的字段或者方法。
java 有几种数据类型?
| 类型 | 字节大小 | 默认值 | 包装类 |
|---|---|---|---|
| byte | 1B | 0 | Byte |
| short | 2B | 0 | Short |
| int | 4B | 0 | Int |
| long | 8B | 0L | Long |
| char | 2B(使用 unicode 作为编码) | '\u0000' | Char |
| float | 4B | 0.0f | Float |
| double | 8B | 0.0d | Double |
| boolean | 1bit | False | Boolean |
什么是枚举类型?
枚举是一种特殊的数据类型,用于定义一组常量,使用 enum 来定义,可以包含 0 个或多个枚举常量。
枚举常量本质上使枚举类的静态实例,编译器会为每个枚举类生成一个私有的构造函数,用于创建枚举常量的实例。枚举常量在内存中只有一个实例,保证唯一性。(枚举式单例)
s=s+1 和 s+=1 的区别?
在 Java 中,s = s + 1 和 s += 1 在大多数情况下功能相同,但在某些特定场景下会有区别,主要涉及 类型转换 和 复合赋值运算符的隐式类型转换。
基本数据类型(int, long, float, double 等
对于基本数据类型(如
int、long、float、double),s = s + 1和s += 1在功能上是等价的,但+=可能更高效(因为 JVM 可能优化为iinc字节码指令)。复合赋值运算符的隐式类型转换
关键区别在于
+=会自动进行类型转换,而s = s + 1不会。s = s + 1要求s + 1的结果类型必须匹配s的类型,否则会编译错误。s += 1会自动进行强制类型转换(即使可能丢失精度)。示例
javabyte b = 10; b = b + 1; // 编译错误!因为 (b + 1) 是 int 类型,不能直接赋值给 byte b += 1; // 正确!相当于 b = (byte)(b + 1)原因:
b + 1会自动提升为int运算,而byte不能直接接收int,所以b = b + 1会报错。b += 1会隐式执行b = (byte)(b + 1),所以能编译通过。
对象类型(如 String)
对于
String类型:s = s + 1会调用StringBuilder进行拼接(s + 1会先转换成String)。s += 1也是调用StringBuilder,但写法更简洁。
& 和 && 的区别?
逻辑判断推荐使用&&,位运算则需要使用&
&和&&都可以用作逻辑与的运算符,表示逻辑与(and),当运算符两边都是 true 时,整个运算结果才为 true,否则只要一方为 false,则结果为 false。
&&还具有短路功能,即如果第一个表达式为 false,则不再计算第二个表达式。
&可以用作位运算符,当&操作符两边的表达式不是 boolean 类型时,&表示按位与操作,通常使用 )0x0f来与一个整数进行&运算符操作,来获取该整数的低四位的值。例如 0x55 & 0x0f =0x05;
| 对比 | & | && |
|---|---|---|
| 类型 | 位运算符,逻辑运算符 | 逻辑运算符 |
| 是否短路 | 不短路,两边都执行 | 短路,左边 false,则右边不执行 |
| 场景 | 位运算,强制执行右侧逻辑 | 用于多重逻辑判断 |
| 效率 | 稍低,可能多执行右边代码 | 高效率 |
| 右边是否一定执行 | 是 | 否 |
| 建议 | 用于必须执行左右两边逻辑的情况 | 用于逻辑判断 |
如何理解值传递和引用传递
**值传递:**传递的是基本类型参数的字面量的拷贝,方法对参数的修改不会影响之前参数的值。
**引用传递:**传递的是该对象在堆内存中的地址拷贝,而不是拷贝整个对象本身,方法对参数的修改会影响参数之前的值
java 支持多继承吗
Java 不支持类的多继承(即一个类不能同时直接继承多个父类),但支持以下两种形式的“多继承”:
单继承 + 接口的多实现
- 类只能单继承(一个类只能直接继承一个父类),但可以实现多个接口。
接口(
interface)可以多继承(一个接口可以继承多个父接口),因此通过接口间接实现了多继承的部分特性。java// 单继承(类) class A {} class B extends A {} // 正确:B 继承 A // 错误示例:类不能多继承 // class C extends A, AnotherClass {} // 编译报错! // 接口的多实现 interface X { void methodX(); } interface Y { void methodY(); } class Z implements X, Y { // 正确:Z 实现多个接口 @Override public void methodX() {} @Override public void methodY() {} } // 接口的多继承(接口可以继承多个父接口) interface W extends X, Y {} // 正确:W 继承 X 和 Y
默认方法的间接多继承
从 Java 8 开始,接口可以定义默认方法(
default方法),实现了该接口的类会继承默认方法如果一个类实现了多个接口,且这些接口有同名的默认方法,需要通过
super或重写解决冲突javainterface Foo { default void print() { System.out.println("Foo"); } } interface Bar { default void print() { System.out.println("Bar"); } } class Baz implements Foo, Bar { // 必须重写冲突的默认方法 @Override public void print() { Foo.super.print(); // 显式选择调用 Foo 的默认方法 } }
重载和重写有什么区别?
方法重写是父类与子类之间的多态性的一种表现,即子类可以覆盖从父类继承的方法,重写一般指使用 @Overried表示,比如接口的实现,或者重写 equals() 和 hashcode()方法
方法重载是一个类中方法多态性的一种表现,即一个类中可以有多个重名的方法,方法的参数类型不同,或者参数的数量不同。
java 异常有哪些分类?
Throwable
Throwable是 java 异常类的顶级根类,所有的异常都继承这个类。Error 和 Exception 是其子类
Error
Error是非程序异常,即程序不能捕获的异常类,一般是编译异常或者系统性的错误,比如内存溢出异常 OutOfMemory.
Exception
Exception 是程序异常类,由程序内部产生,Exception 可分为运行时异常、非运行时异常。
运行时异常
java 编译器不会检查它,程序中可能出现这类异常,即使没有 try catch 语句捕获,也没有用 throws 子句声明抛出它,也会编译通过,运行时异常可处理或者不处理。
非运行时异常
非运行时异常是程序必须进行处理的异常,捕获或者抛出,如果不处理程序就不能编译通过。如常见的 IOException、ClassNotFoundException 等。
怎么理解 java 中的多态机制
多态就是让“看起来一样”的代码,在运行时表现出“不一样”的行为。
多态的三大前提
java 实现多态必须满足三个条件:
继承(或实现接口)
方法重写(子类重写父类的方法)
父类引用指向子类对象
javaclass Animal { void speak() { System.out.println("Animal speaks"); } } class Dog extends Animal { @Override void speak() { System.out.println("Dog barks"); } } public class Main { public static void main(String[] args) { Animal a = new Dog(); // 父类引用指向子类对象 a.speak(); // 输出:Dog barks } }
编译时类型 VS 运行时类型 (编译看左边,运行看右边)
编译时看引用类型(决定能访问的方法签名)
运行时看实际对象类型(决定调用哪个方法实现)
javaAnimal a = new Dog();//编译时 a 是 Animal类型 a.speak();//编译时允许调用speak,运行时执行Dog 的speak()
多态的好处
- 提高代码拓展性:新增子类无需修改父类代码。
- 便于维护和解耦:使用统一的父类引用即可操作多个子类。
- 实现面向接口编程而非面向实现编程:程序更加面向抽象设计。
Equals 和 hashCode 的区别和联系?
关于 hashCode 和 equals方法是有一些常规协定
两个对象用 equals 比较返回 true,那么这两个对象的 hashCode 返回值一定相同,如果重写 equals 方法就必须重写 hashCode() 方法。
两个对象的 equals 比较返回 false,不要求 hashCode() 方法也一定返回不同的值,但是最好是返回不同的值。
hashCode 返回的是对象的哈希值(也叫散列码、散列值)。返回的是一个 int类型的整数。在 java中,hashCode 的默认实现是 native 方法,返回的值与对象的内存地址有关
pubic native int hashCode();这个 native 方法在底层 jvm中通常是根据对象的内存地址或者对象头的某些字段来生成一个整型值作为 hashCode
所以,如果你没有重写 hashCode(),那么默认的返回值可能和内存地址有关,但并不一定等于内存地址。而且在不同的 jvm中可能有不同的实现。
