《JAVA面经实录》- JAVA

一 、基础

1. 介绍下Java中的基本数据类型

基本类型

位数

大小(字节)

默认值

取值范围

封装类

byte

8

1

(byte)0

-128 ~ 127

Byte

short

16

2

(short)0

-32768(-2^15) ~ 32767(2^15 - 1)

Short

int

32

4

0

-2147483648 ~ 2147483647

Integer

long

64

8

0l

-9223372036854775808(-2^63) ~ 9223372036854775807(2^63 -1)

Long

float

32

4

0.0f

1.4E-45 ~ 3.4028235E38

Float

double

64

8

0.0d

4.9E-324 ~ 1.7976931348623157E308

Double

boolean

1

-

false

true、false

Boolean

char

16

2

\u0000(null)

0 ~ 65535(2^16 - 1)

Character

boolean: int 4个字

需要注意:

(1) int是基本数据类型,Integer是int的封装类,是引用类型。int默认值是0,而Integer默认值是null,所以Integer能区分出0和null的情况。一旦java看到null,就知道这个引用还没有指向某个对象,再任何引用使用前,必须为其指定一个对象,否则会报错。

(2) 基本数据类型在声明时系统会自动给它分配空间,而引用类型声明时只是分配了引用空间,必须通过实例化开辟数据空间之后才可以赋值。数组对象也是一个引用对象,将一个数组赋值给另一个数组时只是复制了一个引用,所以通过某一个数组所做的修改在另一个数组中也看的见。

(3)Java 里使用 long 类型的数据一定要在数值后面加上 L,否则将作为整型解析。

(4)Java 里使用 float 类型的数据一定要在数值后面加上 f 或 F,否则将无法通过编译。

(5)char a = 'h' : 单引号,String a = "hello" : 双引号。

2.自增自减运算符

自增运算符:

无论这个变量是否参与到运算中去,只要用++运算符,这个变量本身就加1操作

只是说如果变量参与到运算中去的话,对运算结果是产生影响:

看++在前还是在后,如果++在后:先运算,后加1;如果++在前,先加1,后运算

自减运算符:

无论这个变量是否参与到运算中去,只要用--运算符,这个变量本身就减1操作

只是说如果变量参与到运算中去的话,对运算结果是产生影响:

看--在前还是在后,如果--在后:先运算,后减1; 如果--在前,先减1,后运算

3. Java有哪几种注释?

Java中的注释主要有三种形式,这三种注释形式在Java编程中都有其特定的用途和场景,合理使用注释可以提高代码的可读性和可维护性:

3.1 单行注释:

a. 使用双斜杠(//)开头,仅在该行有效。

b. 适用于对少量代码或特定代码行进行简单说明。

c. 示例:// 这是一个单行注释。

3.2 多行注释:

a. 使用/* 开始,以 */结束,可以跨越多行。

b. 适用于对一大段代码或复杂逻辑进行详细解释。

c. 示例:/* 这是一个多行注释,它可以跨越多行 */。

3.3 文档注释:

a. 使用/** 开始,以 */结束,通常出现在类、方法、字段等的声明前面。

b. 用于生成代码文档,如API文档,这种注释可以被工具(如Javadoc)提取并生成相应的文档。

c. 文档注释可以包含特定的标签。

4. Java中的关键字有哪些?

Java关键字一共有53个,其中包括48个关键字、2个保留字和3个特殊直接量。具体来说:

  • 48个关键字

访问修饰符publicprotectedprivate

类、接口、抽象类和实现接口、继承类、实例化对象:classinterfaceabstractimplementsextendsnew

包相关:importpackage

数据类型:bytecharbooleanshortintfloatlongdoublevoid

条件循环:ifelsewhileforswitchcasedefaultdobreakcontinuereturninstanceof

修饰方法、类、属性和变量:staticsuperfinalthisnativestrictfpsynchronizedtransientvolatile

错误处理:catchtryfinallythrowthrows

其他enumassert

  • 2个保留字

constgoto(这两个保留字在Java中目前没有实际用途,但可能在未来的Java版本中被用作关键字)

  • 3个特殊直接量

nulltruefalse

5. & 和 && 的区别?

&运算符是:逻辑与;&&运算符是:短路与。

1)&和&&在程序中最终的运算结果是完全一致的,只不过&&存在短路现象。如果是&运算符,那么不管左边的表达式是true还是false,右边表达式是一定会执行的。当&&运算符左边的表达式结果为false的时候,右边的表达式不执行,此时就发生了短路现象,也就是说&&会更加的智能。这就是他们俩的本质区别。

2)当然,&运算符还可以使用在二进制位运算上,例如按位与操作。

6. char 型变量中能不能存储一个中文汉字,为什么?

char 类型可以存储一个中文汉字,因为Java中使用的编码是Unicode编码,一个char 类型占2个字节(16 比特),所以放一个中文是没问题的。

补充:使用Unicode 意味着字符在JVM内部和外部有不同的表现形式,在JVM内部都是 Unicode,当这个字符被从JVM内部转移到外部时(例如存入文件系统中),需要进行编码转换。所以 Java 中有字节流和字符流,以及在字符流和字节流之间进行转换的转换流,如 InputStreamReader和OutputStreamReader,这两个类是字节流和字符流之间的适配器类,承担了编码转换的任务。

7. break ,continue ,return 的区别及作用

break 跳出总上一层循环,不再执行循环(结束当前的循环体) 。

continue 跳出本次循环,继续执行下次循环(结束正在执行的循环 进入下一个循环条件) 。

return 程序返回,不再执行下面的代码(结束当前的方法 直接返回) 。

另外在switch...case语句中,break不是必须的,但是会较大影响到运行结果。如果没有break,后面的一旦有case匹配成功,后面的case将无条件的向下执行其它的case

8. switch 是否能作用在 byte 上,能否作用在 long 上,能否作用在 String 上?

早期的 JDK 中,switch(expr)中,expr 可以是 byte、short、char、int。从 1.5 版开始,Java 中引入了枚举类型(enum),

expr 也可以是枚举,从 JDK 1.7 版开始,还可以是字符串(String)。长整型(long)是不可以的。

9. Math.round(11.5)等于多少?Math.round(- 11.5) 又等于多少?

Math.round(11.5)的返回值是12,Math.round(-11.5)的返回值是-11。四舍五入的原理是在参数上加0.5然后进行取整。

10. short s1 = 1; s1 = s1 + 1; 有错吗?short s1 = 1; s1 += 1 有错吗?

前者不正确,后者正确。

对于 short s1 = 1; s1 = s1 + 1;由于 1 是 int 类型,因此 s1+1 运算结果也是 int 型,需要强制转换类型才能赋值给 short 型。

而 short s1 = 1; s1 += 1;可以正确编译,因为 s1+= 1;相当于 s1 = (short)(s1 + 1);其中有隐含的强制类型转换。

11.数组有没有length()方法?String有没有length()方法?

Java中的数组没有length()方法,但是有length属性。String有length()方法。

12. 用最有效率的方法计算2乘以8?

2 << 3,将2左移3位

案例: 4乘以8最快的方式: 4<<3

  • << 左移

3<<2 = 12

面试题: 4乘以8最快的方式: 4<<3

  • >> 有符号右移

6>>2 = 1

  • >>> 无符号右移

6>>>2 = 1

13. 下面 Integer 类型的数值比较输出的结果为?

public class Test{
    public static void main(String[] args) {
       Integer f1 = 100, f2 = 100, f3 = 150, f4 = 150; System.out.println(f1 == f2);
       System.out.println(f3 == f4);
    }
}

f1==f2的结果是 true,而f3==f4 的结果是false。为什么呢?先来说说装箱的本质。当我们给一个Integer 对象赋一个 int 值的时候,会调用 Integer 类的静态方法 valueOf,如果看看valueOf的源代码就知道发生了什么。如果整型字面量的值在-128 到 127 之间,那么不会 new 新的 Integer 对象,而是直接引用常量池中的Integer对象,所以上面的面试题中f1==f2的结果是 true,而f3==f4 的结果是false。

14. int 和 Integer 有什么区别?

Java 的JDK从 1.5 开始引入了自动装箱/拆箱机制。它为每一个基本数据类型都引入了对应的包装类型(wrapper class),int的包装类就是 Integer,其它基本类型对应的包装类如下:原始类型: boolean,char,byte,short,int,long,float,double包装类型:Boolean,Character,Byte,Short,Integer,Long,Float,Double。

15. 什么是方法的返回值?返回值的作用是什么?

方法的返回值是指我们获取到的某个方法体中的代码执行后产生的结果!(前提是该方法可能 产生结果)。返回值的作用:接收出结果,使得它可以用于其他的操作!

16. 如何将数值型字符转换为数字?

调用数值类型相应包装类中的方法 parse***(String)或 valueOf(String) 即可返回相应基本类型或包装类型数值;

17. 如何将数字转换为字符?

将数字与空字符串相加即可获得其所对应的字符串;另外对于基本类型 数字还可调用 String 类中的 valueOf(…)方法返回相应字符串,而对于包装类型数字则可调用其 toString()方法获得相应字符串;

18. 如何取小数点前两位并四舍五入?

可用该数字构造一 java.math.BigDecimal 对象,再利用其 round()方法 进行四舍五入到保留小数点后两位,再将其转换为字符串截取最后两位。

19. 3*0.1 == 0.3 将会返回什么? true 还是 false?

false,因为有些浮点数不能完全精确的表示出来。

20 . Java移位运算符?

java中有三种移位运算符 

<< :左移运算符,x << 1,相当于x乘以2(不溢出的情况下),低位补0 

>> :带符号右移,x >> 1,相当于x除以2,正数高位补0,负数高位补1 

>>> :无符号右移,忽略符号位,空位都以0补齐

21. 基本类型和包装类型的区别?

Java中的基本类型(Primitive Types)和包装类型(Wrapper Classes)之间存在多个关键区别,这些区别主要体现在以下方面:

一、包含内容与性质

  • 基本类型:只包含数据本身,不包含任何方法或操作。它们不是对象,因此没有对象的特性,如继承和多态。
  • 包装类型:不仅包含数据,还包含了一系列的方法(如类型转换、比较等)和属性,是对基本类型数据的封装。包装类型是对象,具有对象的所有特性,如继承自Object类的功能(如toString()、equals()等)。

二、声明方式与存储位置

  • 基本类型:直接声明变量并赋值,不需要使用new关键字。它们直接将值保存在栈内存中,访问速度较快。
  • 包装类型:需要使用new关键字在堆内存中分配内存空间,或者使用自动装箱(JDK 5及以上版本支持)来创建对象。包装类型将对象放在堆内存中,通过栈中的引用来调用它们,访问速度相对较慢,并且需要考虑垃圾回收等额外开销。

三、初始值

  • 基本类型:在声明时如果没有显式赋值,则会被赋予一个默认值。例如,int的默认值为0,boolean的默认值为false。
  • 包装类型:在声明时如果没有显式赋值,则默认值为null,因为它们是引用类型。

四、使用方式

  • 基本类型:直接用于数值计算、位运算等场景,效率较高。但由于它们不是对象,因此不能在需要对象的场合(如集合中)直接使用。
  • 包装类型:主要用于需要对象的地方,如集合(List、Map等)中只能存储对象,因此基本类型需要通过包装类来转换为对象才能存储在集合中。此外,包装类型还提供了丰富的操作方法和常量。

五、泛型适用性

  • 基本类型:不能直接用于泛型,因为泛型在编译时会进行类型擦除,而基本类型没有对应的类型信息可以擦除。
  • 包装类型:可以用作泛型的类型参数,因为它们是对象类型,具有类型信息。例如,可以使用List<Integer>来存储整数对象。

六、内存占用与性能

  • 基本类型:通常占用较少的内存空间,因为它们只存储数据本身。
  • 包装类型:由于它们是对象,因此需要额外的内存来存储对象头和引用等信息。这可能导致在大量使用包装类型时增加内存开销。此外,自动装箱和拆箱操作也会消耗一定的性能。

综上所述,Java中的基本类型和包装类型在包含内容、性质、声明方式、存储位置、初始值、使用方式、泛型适用性、内存占用与性能等方面都存在明显的区别。在开发中,应根据具体需求选择合适的类型以提高代码的可读性、可维护性和性能。

22. 包装类型的缓存机制了解么?

Java包装类型的缓存机制是Java中一个重要的性能优化手段。以下是对Java包装类型缓存机制的详细解释:

一、缓存机制概述

Java中的包装类(Wrapper Class)是为了将基本数据类型转换为对象而存在的。包装类都位于java.lang包中,使用时无需显式导入。包装类型缓存机制指的是,在某些情况下,Java会对一定范围内的包装类对象进行缓存,以减少对象的创建和销毁,从而提高性能和节省内存空间。

二、缓存机制的实现

包装类型的缓存机制是通过静态成员变量来实现的。在Integer、Long、Short、Byte、Character这五个包装类中,定义了一个静态数组cache[],用于缓存常用的数值对象。当使用valueOf()方法创建包装类对象时,会先检查该值是否在缓存范围内。如果是,则直接返回缓存中的对象;否则,创建一个新的对象并可能放入缓存中(注意,对于超出缓存范围的新对象,有的包装类并不会将其放入缓存,这取决于具体的实现)。

三、各包装类的缓存范围

  1. Integer:默认缓存了-128到127之间的整数。这个范围是根据实际应用中整型数据的常用范围来设定的,能够覆盖大多数常用情况。
  2. Long:默认缓存了-128到127之间的长整数。
  3. Short:默认缓存了-128到127之间的短整数。
  4. Byte:默认缓存了-128到127之间的字节。由于byte的值范围本身就是-128到127,所以所有的Byte对象都使用缓存。
  5. Character:默认缓存了0到127之间的字符。这是因为ASCII字符集只定义了128个字符,而Unicode字符集的前128个字符与ASCII字符集完全相同。
  6. Boolean:只缓存了true和false两个对象。

需要注意的是,浮点数类型的包装类(Float和Double)并没有实现缓存机制,主要是因为浮点数的表示范围非常大,且使用场景多样,缓存效果并不明显。

23. 自动装箱与拆箱了解吗?原理是什么?

Java中的自动装箱与拆箱是Java 5引入的一项特性,它允许Java编译器在需要时自动地将基本数据类型转换为对应的包装类类型(自动装箱),或者将包装类类型转换为对应的基本数据类型(自动拆箱)。这一特性极大地简化了代码编写,提高了代码的可读性和可维护性。

1. 自动装箱的原理:

当需要将一个基本数据类型(如int、char等)赋值给一个对应的包装类类型(如Integer、Character等)的变量时,Java编译器会自动调用该包装类的valueOf()方法,将基本数据类型转换为包装类对象。例如,Integer i = 100; 这行代码实际上会被编译器转换为 Integer i = Integer.valueOf(100);。

2. 自动拆箱的原理:

当需要将一个包装类类型的变量赋值给一个基本数据类型(或其对应的包装类类型的变量参与基本数据类型的运算或比较)时,Java编译器会自动调用该包装类的xxxValue()方法(如intValue()、charValue()等),将包装类对象转换为对应的基本数据类型。例如,int j = i; 这行代码(假设i是一个Integer类型的变量)实际上会被编译器转换为 int j = i.intValue();

24. 超过 long 整型的数据应该如何表示?

  1. 使用BigInteger类:BigIntegerjava.math包中的一个类,它提供了任意精度的整数运算。你可以使用BigInteger来表示和操作非常大的整数。
  2. 使用BigDecimal类(如果涉及小数):如果你需要表示非常大的小数,可以使用BigDecimal类。它也是java.math包中的一个类,提供了任意精度的浮点数(实际上是定点数)运算。

二、面向对象

1. 面向过程语言和面向对象语言的区别

面向过程:是分析解决问题的步骤,然后用函数把这些步骤一步一步地实现,然后在使用的时候一一调用则可。性能较高,所以单片机、嵌入式开发等一般采用面向过程开发

面向对象:是把构成问题的事务分解成各个对象,而建立对象的目的也不是为了完成一个个步骤,而是为了描述某个事物在解决整个问题的过程中所发生的行为。面向对象有封装、继承、多态的特性,所以易维护、易复用、易扩展。可以设计出低耦合的系统。 但是性能上来说,比面向过程要低。

2.java面向对象的理解?面向对象的特征?

整体上是封装、继承、多态、抽象。

首先面向对象是一种思想。在java中万事万物皆对象。类是对相同事物的一种抽象、是不可见的,对象具体的、可见的。由对象到类的过程是抽象的过程,由类到对象的过程是实例化的过程。面向对象的三大特征分别是封装、继承和多态。 

封装隐藏了类的内部实现机制,对外界而言它的内部细节是隐藏的,暴露给外界的只是它的访问方法。例如在属性的修饰符上我们往往用的private私有的,这样其它类要想访问就通过get和set方法。因此封装可以程序员按照既定的方式调用方法,不必关心方法的内部实现,便于使用; 便于修改,增强 代码的可维护性。 

继承在本质上是特殊~一般的关系,即常说的is-a关系。子类继承父类,表明子类是一种特殊的父类,并且具有父类所不具有的一些属性或方法。比如从猫类、狗类中可以抽象出一个动物类,具有和猫、狗、虎类的共同特性(吃、跑、叫等)。通过extends关键字来实现继承。Java中的继承是单继承,即一个子类只允许有一个父类。

 Java多态是指的是首先两个类有继承关系,其次子类重写了父类的方法,最后父类引用指向子类对象。如Animal a=new Dog();这行代码就体现了多态。

总是:

  • 封装:把客观事物封装成抽象的类,封装可以隐藏实现细节,使得代码模块化
  • 继承:继承是指可以使用现有类的所有功能,并在无需重新编写原来的类的情况下对这些功能进行扩展。创建的新类称为“子类”或“派生类”。被继承的类称为“基类”、“父类”或“超类”。目的也是为了代码重用。
  • 多态:包括覆盖(重写)和重载,重写,是指子类重新定义父类的方法。重载,多个方法同名不同参(参数个数、参数类型等)。

3.介绍一下Object类中的方法?

(1). getClass 方法

方法: public final native Class<?> getClass();

描述:final 方法、获取对象的运行时 class 对象,class 对象就是描述对象所属类的对象。

这个方法通常是和 Java 反射机制搭配使用的。

(2). hashCode 方法

方法:public native int hashCode();

描述:该方法主要用于获取对象的散列值。Object 中该方法默认返回的是对象的堆内存地址。

(3). equals 方法

方法:public boolean equals(Object obj) { return (this == obj);}

描述:该方法用于比较两个对象,如果这两个对象引用指向的是同一个对象,那么返回 true,否则返回 false。一般 equals 和 == 是不一样的,但是在 Object 中两者是一样的。子类一般都要重写这个方法。

重写equals为什么还要重写hashcode:

1)、为了保证一个原则,equals相同的两个对象hashcode必须相同。如果重写了equals而没有重写hashcode,会出现equals相同hashcode不相同这个现象。

2)、在散列集合中,是使用hashcode来计算key应存储在hash表的索引,如果重写了equals而没有重写hashcode,会出现两个完全相同的两个对象,hashcode不同,计算出的索引不同,那么这些集合就乱套了。

3)、提高效率,当我们比较两个对象是否相同的时候,先比较hashcode是否相同,如果hashcode不相同肯定不是一个对象,如果hashcode相同再调用equals来进行比较,减少比较次数提高效率。

(4). clone 方法

方法:protected native Object clone() throws CloneNotSupportedException;

描述:该方法是保护方法,实现对象的浅复制,只有实现了 Cloneable 接口才可以调用该方法,否则抛出 CloneNotSupportedException 异常。

当你使用 clone() 方法克隆一个对象时,会创建一个新的对象,但是对象的引用类型字段仍然指向相同的对象。

而对于基本类型字段和String,Java 中的自动装箱和拆箱特性会使得对象在被克隆时被重新分配内存,从而修改副本对象中的该字段不会影响原对象。

(5). toString 方法

方法:public String toString() { return getClass().getName() + "@" + Integer.toHexString(hashCode()); }

描述:返回一个 String 对象,一般子类都有覆盖。默认返回格式如下:对象的 class 名称 + @ + hashCode 的十六进制字符串。

. notify 方法

方法:public final native void notify();

描述:final 方法,主要用于唤醒在该对象上等待的某个线程。

(7). notifyAll 方法

方法:public final native void notifyAll();

描述:final 方法,主要用于唤醒在该对象上等待的所有线程。

(8). wait(long timeout)

方法:public final native void wait(long timeout) throws InterruptedException;

描述:wait 方法就是使当前线程等待该对象的锁,当前线程必须是该对象的拥有者,也就是具有该对象的锁。wait() 方法一直等待,直到获得锁或者被中断。wait(long timeout) 设定一个超时间隔,如果在规定时间内没有获得锁就返回。

(9). wait(long timeout, int nanos)

方法:

public final void wait(long timeout, int nanos) throws InterruptedException {

if (timeout < 0) {

throw new IllegalArgumentException("timeout value is negative");

}

if (nanos < 0 || nanos > 999999) {

throw new IllegalArgumentException( "nanosecond timeout value out of range");

}

if (nanos >= 500000 || (nanos != 0 && timeout == 0)) {

timeout++;

}

wait(timeout);

}

描述:

timeout:最大等待时间(毫秒)

nanos:附加时间在毫秒范围(0-999999)

该方法导致当前线程等待,直到其他线程调用此对象的 notify() 方法或notifyAll()方法,或在指定已经过去的时间。此方法类似于 wait 方法的一个参数,但它允许更好地控制的时间等待一个通知放弃之前的量。实时量,以毫微秒计算,计算公式如下:1000000 * timeout + nanos

在所有其他方面,这种方法与 wait(long timeout) 做同样的事情。特别是 wait(0, 0) 表示和 wait(0) 相同。

(10). wait 方法

方法:public final void wait() throws InterruptedException { wait(0);}

描述:可以看到 wait() 方法实际上调用的是 wait(long timeout) 方法,只不过 timeout 为 0,即不等待。

(11). finalize 方法

方法:protected void finalize() throws Throwable { }

描述:该方法是保护方法,主要用于在 GC 的时候再次被调用,如果我们实现了这个方法,对象可能在这个方法中再次复活,从而避免被 GC 回收。

4.Java中实现多态的机制是什么?

Java中的多态靠的是父类或接口定义的引用变量可以指向子类或具体实现类的实例对象,而程 序调用的方法在运行期才动态绑定,就是引用变量所指向的具体实例对象的方法,也就是内存 里正在运行的那个对象的方法,而不是引用变量的类型中定义的方法。

5.this与super的区别?

super:它引用当前对象的直接父类中的成员(用来访问直接父类中被隐藏的父类中成员数据或函数,基类与派生类中有相同成员定义时如:super.变量名 super.成员函数据名 (实参)

this:它代表当前对象名(在程序中易产生二义性之处,应使用this来指明当前对象;如果函数的形参与类中的成员数据同名,这时需用this来指明成员变量名)

super()和this()区别:

[1]super()在子类中调用父类的构造方法,this()在本类内调用本类的其它构造方法。

[2]super()和this()均需放在构造方法内第一行。尽管可以用this调用一个构造器,但却不能调用两个。

[3]this和super不能同时出现在一个构造函数里面,因为this必然会调用其它的构造函数,其它的构造函数必然也会有super语句的存在,所以在同一个构造函数里面有相同的语 句,就失去了语句的意义,编译器也不会通过。

[4]this()和super()都指的是对象,所以,均不可以在static环境中使用。包括:static变量,static方法,static语句块。从本质上讲,this是一个指向本对象的指针, 然而super是一个Java关键字。

6.Java中重写和重载有哪些区别?

方法的重载和重写本质都是实现多态的方式,区别在于前者实现的是编译时的多态性,而后者实现的是运行时的多态。

方法重载的规则:

1)方法名一致,

2)参数列表不同(参数顺序不同或者参数类型不同或者参数个数不同)。

3)重载与方法的返回值无关,这个很关键。

方法重写的规则:

1)参数列表和返回值类型必须完全与父类的方法一致。

2)构造方法不能被重写,声明为 final 的方法不能被重写,声明为 static 的方法不能被重写,但是能够被再次声明。

3)访问权限不能比父类中被重写的方法的访问权限更低。

4)重写的方法能够抛出任何检查异常(编译时异常),但是重写的方法不能抛出比被重写方法声明的更广泛的运行时异常。

7.接口和抽象类有哪些区别?

抽象类

抽象类:使用abstract修饰;不能实例化;含有抽象方法的类是抽象类;抽象类可以含有抽象方法,也可以不包含抽象方法,抽象类中可以有具体的方法;如果一个子类实现了父类(抽象类)的所有抽象方法,那么该子类可以不必是抽象类,否则就是抽象类;

接口

接口:接口使用interface修饰;接口不能被实例化;一个类只能继承一个类,但是可以实现多个接口;

在jdk8之前,interface之中可以定义变量和方法,变量必须是public、static、final的,方法必须是public、abstract的。

JDK8及以后,允许我们在接口中定义static方法和default方法。

如果一个实现类实现了两个接口,两个接口中有同名的静态方法,不会产生错误,因为jdk8只能通过接口类调用接口中的静态方法,所以对编译器来说是可以区分的,接口中的静态方法可以在接口默认方法中调用,实现类的方法可以调用,但是不能通过实现类名及实现类对象来调用。

如果两个接口中定义了一模一样的默认方法,并且一个实现类同时实现了这两个接口,那么必须在实现类中重写默认方法,否则编译失败。

接口更多的是在系统架构设计方法发挥作用,主要用于定义模块之间的通信契约。而抽象类在代码实现方面发挥作用,可以实现代码的重用。

8.接口是否可继承(extends)接口? 抽象类是否可实现(implements) 接口? 抽象类是否可继承具体类(concrete class)?

接口可以继承接口。抽象类可以实现(implements)接口,抽象类可继承具体类,但前提是具体类必须有明确的构造函数。

9.描述一下值传递和引用传递的区别?

值传递是指在调用函数时将实际参数复制一份到函数中,这样的话如果函数对其传递过来的形式参数进行修改,将不会影响到实际参数。

引用传递是指在调用函数时将对象的地址直接传递到函数中,如果在对形式参数进行修改,将影响到实际参数的值。

10.Java中==和equals有哪些区别?

equals 和== 最大的区别是一个是方法一个是运算符。1)基本类型中,==比较的是数值是否相等。equals方法是不能用于基本数据类型数据比较的,因为基本数据类型压根就没有方法。2)引用类型中,==比较的是对象的地址值是否相等。equals方法比较的是引用类型的变量所指向的对象的地址是否相等。应为String这个类重写了equals方法,比较的是字符串的内容。

11.hashCode()方法的作用?

hashCode() 的作用是获取哈希码,也称为散列码;它实际上是返回一个int整数。这个哈希码 的作用是确定该对象在哈希表中的索引位置。hashCode() 定义在JDK的Object.java中,这就 意味着Java中的任何类都包含有hashCode()函数。

12.hashcode方法和equals方法区别?

在Java中,每个对象都可以调用自己的hashCode方法得到自己的哈希值(hashCode),相当于对象的指纹信息,通常说世界上没有完全一样的指纹,但是在Java中没有这么绝对,我们依然可以用hashCode值来做一些提前的判断。

1)如果两个对象的hashCode值不一样,那么他们肯定是不同的两个对象;

2)如果两个对象的hashCode值一样,也不代表就是同一个对象;

3)如果两个对象的equals方法相等,那么他们的hashCode值一定相等。

在Java的一些集合类的实现中,在比较两个对象的值是否相等的时候,会根据上面的基本原则,先调用对象的hashCode值来进行比较,如果hashCode值不一样,就可以认定这是两个不一样的数据,如果hashCode值相同,我们会进一步调用equals()方法进行内容的比较。

13.为什么重写 equals 方法必须重写 hashcode 方法?

因为两个相等的对象的 hashCode 值必须是相等。也就是说如果 equals 方法判断两个对象是相等的,那这两个对象的 hashCode 值也要相等。

如果重写 equals() 时没有重写 hashCode() 方法的话就可能会导致 equals 方法判断是相等的两个对象,hashCode 值却不相等。

思考:重写 equals() 时没有重写 hashCode() 方法的话,使用 HashMap 可能会出现什么问题。

总结

  • equals 方法判断两个对象是相等的,那这两个对象的 hashCode 值也要相等。
  • 两个对象有相同的 hashCode 值,他们也不一定是相等的(哈希碰撞)。

14.两个对象值相同(x.equals(y) == true),但却可有不同的hash code,这句话对不对?

不对,如果两个对象x和y满足x.equals(y) == true,它们的哈希码(hash code)应当相同。

Java对于eqauls方法和hashCode方法是这样规定的:

(1)如果两个对象相同(equals方法返回true),那么它们的hashCode值一定要相同;

(2)如果两个对象的hashCode相同,它们并不一定相同。

当然,你未必要按照要求去做,但是如果你违背了上述原则就会发现在使用容器时,相同的对 象可以出现在Set集合中,同时增加新元素的效率会大大下降(对于使用哈希存储的系统,如 果哈希码频繁的冲突将会造成存取性能急剧下降)。

15.有没有可能两个不相等的对象有相同的hashcode

能.在产生hash冲突时,两个不相等的对象就会有相同的 hashcode 值.当hash冲突产生时,一般

有以下几种方式来处理:

  • 拉链法:每个哈希表节点都有一个next指针,多个哈希表节点可以用next指针构成一个单向链表,被分配到同一个索引上的多个节点可以用这个单向链表进行存储.
  • 开放定址法:一旦发生了冲突,就去寻找下一个空的散列地址,只要散列表足够大,空的散列地址总能找到,并将记录存入
  • 再哈希:又叫双哈希法,有多个不同的Hash函数.当发生冲突时,使用第二个,第三个….等哈希函数计算地址,直到无冲突

16.抽象的(abstract)方法是否可同时是静态的(static), 是否可同时是本地方法(native),是否可同时被 synchronized?

都不能。

1)抽象方法需要子类重写,而静态的方法是无法被重写的,因此二者是矛盾的。

2)本地方法是由本地代码(如 C++ 代码)实现的方法,而抽象方法是没有实现的,也是矛盾的。

3)synchronized 和方法的实现细节有关,抽象方法不涉及实现细节,因此也是相互矛盾的。

17.final关键字的用法?

修饰类:当用final修饰一个类时,表明这个类不能被继承。正如String类是不能被继承的。final类中的成员变量可以根据需要设为final,但是要注意final类中的所有成员方法都会被隐式地指定为final方法。

修饰方法:使用final修饰方法的原因有两个。第一个原因是把方法锁定,以防任何继承类修改它的含义;第二个原因是效率。在早期的Java实现版本中,会将final方法转为内嵌调用。但是如果方法过于庞大,可能看不到内嵌调用带来的任何性能提升。在最近的Java版本中,不需要使用final方法进行这些优化了。因此,只有在想明确禁止该方法在子类中被覆盖的情况下才将方法设置为final。(注:一个类中的private方法会隐式地被指定为final方法)

修饰变量:对于被final修饰的变量,如果是基本数据类型的变量,则其数值一旦在初始化之后便不能更改;如果是引用类型的变量,则在对其初始化之后便不能再让其指向另一个对象。虽然不能再指向其他对象,但是它指向的对象的内容是可变的。

18.final与finally、finalize 的区别?

1)final:用于声明属性,方法和类,分别表示属性不可变,方法不可覆盖,被其修饰的类不可继承。

2)finally:异常处理语句结构的一部分,表示总是执行。

3)finalize:Object 类的一个方法,当java对象没有更多的引用指向的时候,系统会自动的由垃圾回收器来负责调用此方法进行回收前的准备工作和垃圾回收。

19.静态变量和实例变量区别?

静态变量: 静态变量由于不属于任何实例对象,属于类的,所以在内存中只会有一份,在类的 加载过程中,JVM只为静态变量分配一次内存空间。

实例变量: 每次创建对象,都会为每个对象分配成员变量内存空间,实例变量是属于实例对象 的,在内存中,创建几次对象,就有几份成员变量。

20.静态方法和实例方法有何不同?

静态方法和实例方法的区别主要体现在两个方面:

在外部调用静态方法时,可以使用"类名.方法名"的方式,也可以使用"对象名.方法名"的 方式。而实例方法只有后面这种方式。也就是说,调用静态方法可以无需创建对象。

静态方法在访问本类的成员时,只允许访问静态成员(即静态成员变量和静态方法), 而不允许访问实例成员变量和实例方法;实例方法则无此限制。

21.访问修饰符public,private,protected,以及不写(默认)时的区别?

修饰符

当前类

同包

子类

其它包

public

protected

默认(缺省)

private

类的成员不写访问修饰时默认为default。默认对于同一个包中的其他类相当于公开 (public),对于不是同一个包中的其他类相当于私有(private)。受保护(protected)对 子类相当于公开,对不是同一包中的没有父子关系的类相当于私有。Java中,外部类的修饰符 只能是public或默认,类的成员(包括内部类)的修饰符可以是以上四种。

22. abstract的方法是否可同时是final、static、native或者synchronized的?

  • final修饰方法表示方法不能被子类重写,但是抽象方法需要被子类重写,所以肯定不能在一起使用。
  • abstract的method 不可以是static的,因为抽象的方法是要被子类实现的,而static与子类扯不上关系!
  • native方法表示该方法要用另外一种依赖平台的编程语言实现的,不存在着被子类实现的问题,所以它也不能是抽象的,不能与abstract混用。
  • abstract是抽象的,而synchronized是同步的,相对于线程讲的;abstract只是声明没有实现,既然没有实现就谈不上同步了,所以不能放到一块使用。当然如果其子类实现了这个方法在子类是可以同步的。

23. 深拷贝和浅拷贝区别了解吗?什么是引用拷贝?

(1)引用拷贝: 引用拷贝不会在堆上创建一个新的对象,只 会在栈上生成一个新的引用地址,最终指向依然是堆上的同一个对象。 (2) 浅拷贝 :浅拷贝会在堆上创建一个新的对象,新对象和原对象不等,但是新对象的属性和老对象相同

其中:

a.如果属性是基本类型(int,double,long,boolean等),拷贝的就是基本类型的值。

b.如果属性是引用类型(除了基本类型都是引用类型),拷贝的就是引⽤数据类型变量的地址值,

⽽对于引⽤类型变量指向的堆中的对象不会拷贝。

(3) 深拷贝 :完全拷贝⼀个对象,在堆上创建一个新的对象,拷贝被拷贝对象的成员变量的值,同时堆中的对象也会拷贝。

24. 介绍下内部类

在Java中,可以将一个类定义在另一个类里面或者一个方法里面,这样的类称为内部类。广泛意义上的内部类一般来说包括这三种:成员内部类、局部内部类、匿名内部类,如下图所示:

25.Java 创建对象有几种方式?

(1)new 关键字

  平时使用的最多的创建对象方式

User user=new User();
​

(2)反射方式

  使用 newInstance(),但是得处理两个异常 InstantiationException、IllegalAccessException:

User user=User.class.newInstance();
Object object=(Object)Class.forName("java.lang.Object").newInstance()
​

(3)clone方法

  Object对象中的clone方法来完成这个操作

(4)反序列化操作

  调用 ObjectInputStream 类的 readObject() 方法。我们反序列化一个对象,JVM 会给我们创建一个单独的对象。JVM 创建对象并不会调用任何构造函数。一个对象实现了 Serializable 接口,就可以把对象写入到文中,并通过读取文件来创建对象。

总结

  创建对象的方式关键字:new、反射、clone 拷贝、反序列化。

全部评论

相关推荐

评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务