基础语法
1. Java有哪些数据类型
定义 : Java 语言是强类型语言,对于每一种数据都定义了明确的具体的数据类型,在内存中分配了不同 大小的内存空间。 细化的数据类型就是占用空间的基本元素。
分类 :
基本数据类型
引用数据类型
- 类(class) eg:String
- 接口(interface)
- 数组([])
1-24-8-4-2-1 空间大小对称记忆
2. switch 是否能作用在 byte 上,是否能作用在 long 上,是否 能作用在 String 上
在 Java 5 以前, switch(expr) 中, expr 只能是 byte 、 short 、 char 、 int 。从 Java5 开始, Java 中引入 了枚举类型,expr 也可以是 enum 类型,从 Java 7 开始, expr 还可以是字符串( String ),但是长整 型(long )在目前所有的版 本中都是不可以的。
3. 用最有效率的方法计算 2 乘以 8
2 << 3 (左移 3 位相当于乘以 2 的 3 次方,右移 3 位相当于除以 2 的 3 次 方)。
public static void main(String[] args) {
System.out.println(2<<3);
}
![2023最新面试题-Java-2_第2张图片](http://img.e-com-net.com/image/info8/5b09b50f72d04c26bef7ace1defacfe8.jpg)
4. Math.round(11.5) 等于多少?Math.round(-11.5) 等于多少
Math.round(11.5) 的返回值是 12 , Math.round(-11.5) 的返回值是 -11 。四舍 五入的原理是在参数上加 0.5 然后进行下取整。 四舍五入 >0.5统一加0.几, <0.5 统一减0.几。
5. float f=3.4;是否正确
![2023最新面试题-Java-2_第3张图片](http://img.e-com-net.com/image/info8/a38805cb0ac24d529d25b5dbb0344d64.jpg)
不正确。 3.4 是双精度数,将双精度型( double )赋值给浮点型( float )属于 下转型( down-casting , 也称为窄化)会造成精度损失,因此需要强制类型转 换float f =(float)3.4; 或者写成 float f =3.4F;
6. short s1 = 1; s1 = s1 + 1;有错吗?short s1 = 1; s1 += 1;有错吗
涉及Java的强制类型转换 支持向上自动转型
![2023最新面试题-Java-2_第4张图片](http://img.e-com-net.com/image/info8/808f9c527b1f4180836ebd7d369916b8.jpg)
对于 short s1 = 1; s1 = s1 + 1; 由于 1 是 int 类型,因此 s1+1 运算结果也是 int 型,需要强制转换类型才 能赋值给 short 型。 而 short s1 = 1; s1 += 1; 可以正确编译,因为 s1+= 1; 相当于 s1 = (short(s1 + 1); 其 中有隐含的强制类型转换。
7.注释
定义:用于解释说明程序的文字
分类
单行注释 格式: // 注释文字
多行注释 格式: /* 注释文字 */
文档注释 格式: /** 注释文字 */
8. 访问修饰符 public,private,protected,以及不写(默认)时的 区别
定义 : Java 中,可以使用访问修饰符来保护对类、变量、方法和构造方法的访 问。 Java 支持 4 种不同的
访问权限。
分类 :
private : 在同一类内可见。使用对象:变量、方法。 注意:不能修饰类(外部 类)
default ( 即缺省,什么也不写,不使用任何关键字) : 在同一包内可见,不使用 任何修饰符。使用
对象:类、接口、变量、方法。
protected : 对同一包内的类和所有子类可见。使用对象:变量、方法。 注意: 不能修饰类(外部
类)。
public : 对所有类可见。使用对象:类、接口、变量、方法
9. final 有什么用?
用于修饰类、属性和方法;
- 被final修饰的类不可以被继承
- 被final修饰的方法不可以被重写
- 被final修饰的变量不可以被改变,被final修饰不可变的是变量的引用,而不是引用指向的内容,引
- 用指向的内容是可以改变的
10. final finally finalize区别
1. final 可以修饰类、变量、方法,修饰类表示该类不能被继承、修饰方法表示该方法不能被重写、修
饰变量表示该变量是一个常量不能被重新赋值。
2. finally 一般作用在 try-catch 代码块中,在处理异常的时候,通常我们将一定要执行的代码方法
finally 代码块中,表示不管是否出现异常,该代码块都会执行,一般用来存放一些关闭资源的代
码。
3. finalize 是一个方法,属于 Object 类的一个方法,而 Object 类是所有类的父类,该方法一般由垃圾
回收器来调用,当我们调用 System.gc() 方法的时候,由垃圾回收器调用 finalize() ,回收垃圾,一
个对象是否可回收的最后判断。
11. this与super的区别
- super: 它引用当前对象的直接父类中的成员(用来访问直接父类中被隐藏的父类中成员数据或函 数,基类与派生类中有相同成员定义时如:super.变量名 super.成员函数据名(实参)
- this:它代表当前对象名(在程序中易产生二义性之处,应使用this来指明当前对象;如果函数的 形参与类中的成员数据同名,这时需用this来指明成员变量名)
- super()和this()类似,区别是,super()在子类中调用父类的构造方法,this()在本类内调用本类的其 它构造方法。
- super()和this()均需放在构造方法内第一行。
- 尽管可以用this调用一个构造器,但却不能调用两个。
- this和super不能同时出现在一个构造函数里面,因为this必然会调用其它的构造函数,其它的构造 函数必然也会有super语句的存在,所以在同一个构造函数里面有相同的语句,就失去了语句的意 义,编译器也不会通过。
- this()和super()都指的是对象,所以,均不可以在static环境中使用。包括:
- static变量,static方法,static语句块。
- 从本质上讲,this是一个指向本对象的指针, 然而super是一个Java关键字。
12. static存在的主要意义
static 的主要意义是在于创建独立于具体对象的域变量或者方法。以致于即使没有创建对象,也能使用属 性和调用方法!
static 关键字还有一个比较关键的作用就是 用来形成静态代码块以优化程序性能。 static 块可以置于类中 的任何地方,类中可以有多个static 块。在类初次被加载的时候,会按照 static 块的顺序来执行每个 static 块,并且只会执行一次。为什么说static 块可以用来优化程序性能,是因为它的特性 : 只会在类加载的时 候 执行一次。因此,很多时候会将一些只需要进行一次的初始化操作都放在static 代码块中进行。
因此比较常见的static应用场景有:
1 、修饰成员变量
2 、修饰成员方法
3 、静态代码块
4 、修饰类【只能修饰内部类也就是静态内部类】
5 、静态导包
static注意事项
1 、静态只能访问静态。 2 、非静态既可以访问非静态的,也可以访问静态的。
13. 抽象类和接口的对比
抽象类是用来捕捉子类的通用特性的。接口是抽象方法的集合。
从设计层面来说,抽象类是对类的抽象,是一种模板设计,接口是行为的抽象,是一种行为的规范。
相同点
- 接口和抽象类都不能实例化
- 都位于继承的顶端,用于被其他实现或继承
- 都包含抽象方法,其子类都必须覆写这些抽象方法
不同点
参 数
|
抽象类
|
接口
|
声明 |
抽象类使用 abstract 关键字声明
|
接口使用 interface 关键字声明
|
实现 |
子类使用 extends 关键字来继承抽象类。如果子类
不是抽象类的话,它需要提供抽象类中所有声明
的方法的实现
|
子类使用 implements 关键字来实现
接口。它需要提供接口中所有声明的
方法的实现
|
构
造
器
|
抽象类可以有构造器
|
接口不能有构造器
|
访
问
修
饰
符
|
抽象类中的方法可以是任意访问修饰符
|
接口方法默认修饰符是 public 。并且
不允许定义为 private 或者
protected
|
多
继
承
|
一个类最多只能继承一个抽象类
|
一个类可以实现多个接口
|
字
段
声
明
|
抽象类的字段声明可以是任意的
|
接口的字段默认都是 static 和 final
的
|
备注 : Java8 中接口中引入默认方法和静态方法,以此来减少抽象类和接口之间 的差异。
14. 抽象类能使用 final 修饰吗?
不能,定义抽象类就是让其他类继承的,如果定义为 final 该类就不能被继承, 这样彼此就会产生矛
盾,所以 final 不能修饰抽象类
15. 创建一个对象用什么关键字?对象实例与对象引用有何不同?
new 关键字, new 创建对象实例(对象实例在堆内存中),对象引用指向对象实 例(对象引用存放在栈 内存中)。一个对象引用可以指向0 个或 1 个对象(一根 绳子可以不系气球,也可以系一个气球) ; 一个 对象可以有n 个引用指向它(可以 用 n 条绳子系住一个气球)
没有对象引用,丢失跟可达性就代表是可以回收的对象。
变量与方法
16. 成员变量与局部变量的区别有哪些
变量:在程序执行的过程中,在某个范围内其值可以发生改变的量。从本质上 讲,变量其实是内存中的 一小块区域
成员变量:方法外部,类内部定义的变量
局部变量:类的方法中的变量。 成员变量和局部 变量的区别。
作用域
成员变量:针对整个类有效。
局部变量:只在某个范围内有效。 ( 一般指的就是方法 , 语句体内 )
存储位置
成员变量:随着对象的创建而存在,随着对象的消失而消失,存储在堆内存中。
局部变量:在方法被调用,或者语句被执行的时候存在,存储在栈内存中。当方法调用完,或者语句结 束后,就自动释放。
生命周期
成员变量:随着对象的创建而存在,随着对象的消失而消失。
局部变量:当方法调用完,或者语句结束后,就自动释放。
初始值
成员变量:有默认初始值。
局部变量:没有默认初始值,使用前必须赋值。
使用原则
在使用变量时需要遵循的原则为:就近原则 首先在局部范围找,有就使用;接着在成员位置找。
17. 静态变量和实例变量区别
静态变量: 静态变量由于不属于任何实例对象,属于类的,所以在内存中只会 有一份,在类的加载过程 中,JVM 只为静态变量分配一次内存空间。
实例变量: 每次创建对象,都会为每个对象分配成员变量内存空间,实例变量 是属于实例对象的,在内 存中,创建几次对象,就有几份成员变量。
18 静态方法和实例方法有何不同?
静态方法和实例方法的区别主要体现在两个方面:
1. 在外部调用静态方法时,可以使用 " 类名 . 方法名 " 的方式,也可以使 用 " 对象名 . 方法名 " 的方式。而 实例方法只有后面这种方式。也就是说,调 用静态方法可以无需创建对象。
2. 静态方法在访问本类的成员时,只允许访问静态成员(即静态成员变量 和静态方法),而不允许访 问实例成员变量和实例方法;实例方法则无此 限制
19. 内部类的分类有哪些
内部类可以分为四种: 成员内部类、局部内部类、匿名内部类和静态内部类。
静态内部类
定义在类内部的静态类,就是静态内部类。
1 public class Outer {
2
3 private static int radius = 1;
4
5 static class StaticInner {
6 public void visit() {
7 System.out.println("visit outer static variable:" + radius);
8 }
9 }
10 }
静态内部类可以访问外部类所有的静态变量,而不可访问外部类的非静态变量; 静态内部类的创建方 式,new 外部类 . 静态内部类 () ,如下:
1 Outer.StaticInner inner = new Outer.StaticInner();
2 inner.visit
成员内部类
定义在类内部,成员位置上的非静态类,就是成员内部类。
1 public class Outer {
2
3 private static int radius = 1;
4 private int count =2;
5
6 class Inner {
7 public void visit() {
8 System.out.println("visit outer static variable:" + radius);
9 System.out.println("visit outer variable:" + count);
10 }
11 }
12 }
成员内部类可以访问外部类所有的变量和方法,包括静态和非静态,私有和公 有。成员内部类依赖于外 部类的实例,它的创建方式外部类实例.new 内部类 () ,如 下:
1 Outer outer = new Outer();
2 Outer.Inner inner = outer.new Inner();
3 inner.visit();
局部内部类
定义在方法中的内部类,就是局部内部类
1 public class Outer {
2
3 private int out_a = 1;
4 private static int STATIC_b = 2;
5
6 public void testFunctionClass(){
7 int inner_c =3;
8 class Inner {
9 private void fun(){
10 System.out.println(out_a);
11 System.out.println(STATIC_b);
12 System.out.println(inner_c);
13 }
14 }
15 Inner inner = new Inner();
16 inner.fun();
17 }
18 public static void testStaticFunctionClass(){
19 int d =3;
20 class Inner {
21 private void fun(){
22 // System.out.println(out_a); 编译错误,定义在静态方法中的局部类不可以访问外
部类的实例变量
23 System.out.println(STATIC_b);
24 System.out.println(d);
25 }
26 }
27 Inner inner = new Inner();
28 inner.fun();
29 }
30 }
定义在实例方法中的局部类可以访问外部类的所有变量和方法,定义在静态方法 中的局部类只能访问外 部类的静态变量和方法。局部内部类的创建方式,在对应 方法内,new 内部类 () ,如下:
1 public static void testStaticFunctionClass(){
2 class Inner {
3 }
4 Inner inner = new Inner();
5 }
匿名内部类
匿名内部类就是没有名字的内部类,日常开发中使用的比较多。
1 public class Outer {
2
3 private void test(final int i) {
4 new Service() {
5 public void method() {
6 for (int j = 0; j < i; j++) {
7 System.out.println("匿名内部类" );
8 }
9 }
10 }.method();
11 }
12 }
13 //匿名内部类必须继承或实现一个已有的接口
14 interface Service{
15 void method();
16 }
除了没有名字,匿名内部类还有以下特点:
- 匿名内部类必须继承一个抽象类或者实现一个接口。
- 匿名内部类不能定义任何静态成员和静态方法。
- 当所在的方法的形参需要被匿名内部类使用时,必须声明为 final。
- 匿名内部类不能是抽象的,它必须要实现继承的类或者实现的接口的所有抽象方 法。
匿名内部类创建方式:
1 new 类/接口{
2 //匿名内部类实现部分
3 }
内部类的优点
- 我们为什么要使用内部类呢?因为它有以下优点:
- 一个内部类对象可以访问创建它的外部类对象的内容,包括私有数据!
- 内部类不为同一包的其他类所见,具有很好的封装性;
- 内部类有效实现了“多重继承”,优化 java 单继承的缺陷。
- 匿名内部类可以很方便的定义回调。
20. 重载(Overload)和重写(Override)的区别。重载的方法能 否根 据返回类型进行区分?
方法的重载和重写都是实现多态的方式,区别在于前者实现的是编译时的多态 性,而后者实现的是运行 时的多态性。
重载:发生在同一个类中,方法名相同参数列表不同(参数类型不同、个数不 同、顺序不同),与方法 返回值和访问修饰符无关,即重载的方法不能根据返回 类型进行区分
重写:发生在父子类中,方法名、参数列表必须相同,返回值小于等于父类,抛 出的异常小于等于父 类,访问修饰符大于等于父类(里氏代换原则);如果父类 方法访问修饰符为private 则子类中就不是重 写。
21. == 和 equals 的区别是什么
== : 它的作用是判断两个对象的地址是不是相等。即,判断两个对象是不是同 一个对象。(基本数据类型 == 比较的是值,引用数据类型 == 比较的是内存地址)
equals() : 它的作用也是判断两个对象是否相等。但它一般有两种使用情况: 情况 1 :类没有覆盖
equals() 方法。则通过 equals() 比较该类的两个对象时, 等价于通过 “==” 比较这两个对象。
情况 2 :类覆盖了 equals() 方法。一般,我们都覆盖 equals() 方法来两个对象 的内容相等;若它们的内 容相等,则返回 true ( 即,认为这两个对象相等 ) 。
举个例子:
1 public class test1 {
2 public static void main(String[] args) {
3 String a = new String("ab"); // a 为一个引用
4 String b = new String("ab"); // b为另一个引用,对象的内容一样
5 String aa = "ab"; // 放在常量池中
6 String bb = "ab"; // 从常量池中查找
7 if (aa == bb) // true
8 System.out.println("aa==bb");
9 if (a == b) // false,非同一对象
10 System.out.println("a==b");
11 if (a.equals(b)) // true
12 System.out.println("aEQb");
13 if (42 == 42.0) { // true
14 System.out.println("true");
15 }
16 }
17 }
说明:
- String中的equals方法是被重写过的,因为object的equals方法是比较的对象的 内存地址,而 String的equals方法比较的是对象的值。
- 当创建String类型的对象时,虚拟机会在常量池中查找有没有已经存在的值和要 创建的值相同的对象,如果有就把它赋给当前引用。如果没有就在常量池中重新创建 一个String对象。
22. hashCode 与 equals (重要)
HashSet如何检查重复
两个对象的 hashCode() 相同,则 equals() 也一定为 true ,对吗?
hashCode 和 equals 方法的关系
面试官可能会问你: “ 你重写过 hashcode 和 equals 么,为什么重写 equals 时 必须重写 hashCode 方 法?
hashCode() 的作用是获取哈希码,也称为散列码;它实际上是返回一个 int 整 数。这个哈希码的作用是 确定该对象在哈希表中的索引位置。hashCode() 定义 在 JDK 的 Object.java 中,这就意味着 Java 中的任何 类都包含有hashCode() 函数。
散列表存储的是键值对 (key-value) ,它的特点是:能根据 “ 键 ” 快速的检索出 对应的 “ 值 ” 。这其中就利用 到了散列码!(可以快速找到所需要的对象)
为什么要有 hashCode
我们以“HashSet 如何检查重复”为例子来说明为什么要有 hashCode:
当你把对象加入 HashSet 时,HashSet 会先计算对象的 hashcode 值来判断对 象加入的位置,同时也 会与其他已经加入的对象的 hashcode 值作比较,如果 没有相符的hashcode,HashSet会假设对象没 有重复出现。但是如果发现有相 同 hashcode 值的对象,这时会调用 equals()方法来检查 hashcode 相 等的对 象是否真的相同。如果两者相同,HashSet 就不会让其加入操作成功。如果不 同的话,就会重 新散列到其他位置。。这样我们就大大减少了 equals 的次数,相应就大大提高了执行速 度。
对象的相等与指向他们的引用相等,两者有什么不同?
- 对象的相等 比的是内存中存放的内容是否相等。
- 引用相等 比较的是他们指向的 内存地址是否相等。