admin 管理员组

文章数量: 887021


2023年12月17日发(作者:language and power)

第一篇 Java基础面试题

1. 说下面向对象四大特性

封装性、继承性、多态性、抽象性。

2. Java语言有些特点

简单性:Java没有像C++那样的指针,运算符重载,类的多继承。并且实现了垃圾的自动回收,简化了程序开发者对于内存管理的工作。

面像对象:对象是指封装数据和操作方法的程序实体。Java提供了简单的类机制以及动态接口。表现形式是封装 继承 多态。

分布式:它有一套很齐全的通信及相关功能的程序库,可以处理TCP/IP协议也可以处理其他的协议。

健壮性:用Java编写的程序能在多种情况下稳定运行。Java在运行和编译的时候都会对可能出现的错误进行检查验证。通过集成异常处理机制,在编译时提示可能出现的但是未被处理的异常,以防止系统的崩溃。

可移植性:Java是与平台无关的,Java类库中也实现了与平台无关的接口,这样类库也是可以移植的。

多线程机制:Java具有多线程机制,使得程序能够并行执行,同步机制也保证了数据的共享,线程也有优先级别,有利于使用线程级别控制不同的任务。

3. 什么是Java程序的主类?应用程序和小程序的主类有何不同?

一个程序中可以有多个类,但只能有一个主类。在Java应用程序中,这个类是指包含main()方法的类。而在Java小程序中,这个主类是一个继承子系统类JApplet或Applet的子类。应用程序的主类不一定要求是public类但小程序的主类必须是public类。主类是Java程序执行的入口点。

简单说应用程序是从主线程启动(也就是 main() 方法)。applet 小程序没有 main() 方法,主要是嵌在浏览器页面上运行(调用init()或者run()来启动),嵌入浏览器这点跟 flash 的小游戏类似。

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

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

5. float f=3.4;是否正确?

不正确。3.4是双精度数,将双精度型(double)赋值给浮点型(float)属于下转型(down-cating,也称为窄化)会造成精度损失,因此需要强制类型转换floatf=(float)3.4;或者写成floatf=3.4F;

6. Java有没有goto?

goto 是Java中的保留字,在目前版本的Java中没有使用。

1

7. &和&&的区别?

1、&&只是编程语言中的符号,只能使用在c、c++、Java、PHP等编程语言中。

2、&符号的使用范围比&&大,它不但能用在编程语言中,它还能用在HTML文档中;表示“and”的意思;表示“联合”的意思;电子制表程序中。

3、&&表示的是“逻辑与”,相当于“和”的意思,即当运算符号两边的表达式的结果都为“正确”的时候,整个运算的结果才是正确的,只要有一方为“错误”,其结果便是“错误”。例12&&13的结果是“1”即“正确”,12&&0的结果是“0”即“错误”。

4、%表示的是“按位与”,即在运算符号两边的表达式的结果都为“正确”时,那结果就是正确的;在运算符号两边的表达式的结果只要有一个为“错误”那结果就是错误的。

8. (11.5) 等于多少?(-11.5)等于多少?

(11.5)==12 (-11.5)==-11 round 方法返回与参数最接近的长整数,参数加 1/2 后求其 floor。

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

2 << 3(左移 3 位相当于乘以 2 的 3 次方,右移 3 位相当于除以 2 的 3 次方)。

10. 什么是Java注释

在Java的编写过程中我们需要对一些程序进行注释,除了自己方便阅读,更为别人更好理解自己的程序,所以我们需要进行一些注释,可以是编程思路或者是程序的作用,总而言之就是方便自己他人更好的阅读。

11. Java有哪些数据类型

有8种原始数据类型:布尔数据类型、字节数据类型、字符数据类型、短数据类型、整数数据类型、长数据类型、浮点数据类型、双数据类型。

12. final 有什么用?

1、final修饰类,表示类不可变,不可扩展(不可继承),即不能有子类

2、final修饰方法,表示该方法不可重写

比如模板设计模式,可以固定我们的算法

3、final修饰变量,这个变量就是常量

注意:

(1)如果修饰的是基本数据类型,这个值本身不能修改

(2)如果修饰的是引用类型,引用的指向不能修改

比如下面的代码是可以的

final Student student = new Student(1,"fengqingyang");

(18);//注意,这个是可以的!

13. final finally finalize的区别

1.简单区别

final用于声明属性,方法和类,分别表示属性不可改变(常量),方法不可覆盖,类不可继承。

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

2

finalize是Object类的一个方法,在垃圾收集器执行的时候会调用被回收对象的此方法,供垃圾收集时的其他资源回收,例如关闭文件等。

2.中等区别

虽然这个单词在Java中都存在,但是并没太多关联:

final:Java中的关键字,修饰符。

A). 如果一个类被声明为final,就意味着它不能再派生出新的子类,不能作为父类被继承。因此,一个类不能同时被声明为abstract抽象类的和final的类。

B). 如果将变量或方法声明为final,可以保证它们在使用中不被改变。

1)被声明为final的变量必须在声明时给定初始值,而在以后的引用中只能读取,不可修改。

2)被声明final的方法只能使用,不能重载。

finally:Java的一种异常处理机制。

finally是对Java异常处理模型的最佳补充。finally结构使代码总会执行,而不管无异常发生。使用finally可以维护对象的内部状态,并可以清理非内存资源。特别实在关闭数据库连接这方面,如果程序员把数据库连接的close()方法放到finally中,就会大大降低程序出错的几率。

finalize:Java中的一个方法名

Java技术使用finalize()方法在垃圾收集器将对象从内存中清除出去前,做必要的清理工作。这个方法是由垃圾收集器在确定这个对象没被引用时对这个对象调用的。它是在Object类中定义的,因此所的类都继承了它。子类覆盖finalize()方法以整理系统资源或者执行其他清理工作。finalize()方法是在垃圾收集器删除对象之前对这个对象调用的。

3.详细区别

这是一道再经典不过的面试题了,我们在各个公司的面试题中几乎都能看到它的身影。final、finally和finalize虽然长得像孪生兄弟,但是它们的含义和用法却是大相径庭。

**final关键字我们首先来说说final。**它可以用于以下四个地方:

1).定义变量,包括静态的和非静态的

2).定义方法的参数

3).定义方法

4).定义类

第一种情况:

如果final修饰的是一个基本类型,就表示这个变量被赋予的值是不可变的,即它是个常量;

如果final修饰的是一个对象,就表示这个变量被赋予的引用时不可变的。

这里需要提醒大家注意的是,不可改变的只是这个变量所保存的引用,并不是这个引用所指向的对象。

第二种情况: final的含义与第一种情况相同。

实际上对于前两种情况,一种更贴切的表述final的含义的描述,那就是,如果一个变量或方法参数被final修饰,就表示它只能被赋值一次,但是Java虚拟机为变量设定的默认值不记作一次赋值。被final修饰的变量必须被初始化。初始化的方式有以下几种:

1.在定义的时候初始化。

变量可以在初始化块中初始化,不可以在静态初始化块中初始化。

3.静态final变量可以在定义时初始化,也可以在静态初始化块中初始化,不可以在初始化块中初始化。

变量还可以在类的构造器中初始化,但是静态final变量不可以。

14. String str = "i" 和String str = new String("1")一样吗?

3

不一样,使用String str=“i”,java虚拟机会把它分配到常量池中,而 String str=new String(“i”)创建了一个对象,会被分到堆内存中。

15. Java 中操作字符串都有哪些类?它们之间有什么区别?

操作字符串的类有:String、StringBuffer、StringBuilder、StringJoiner(JDK1.8)。

String 和 StringBuffer、StringBuilder 的区别在于 String 声明的是不可变的对象,每次操作都会生成新的 String 对象,然后将指针指向新的 String 对象,而 StringBuffer、StringBuilder 可以在原有对象的基础上进行操作,所以在经常改变字符串内容的情况下最好不要使用 String。

StringBuffer 和 StringBuilder 最大的区别在于,StringBuffer 是线程安全的,而 StringBuilder 是非线程安全的,但 StringBuilder 的性能却高于 StringBuffer,所以在单线程环境下推荐使用 StringBuilder,多线程环境下推荐使用 StringBuffer。

16. Java中为什么要用 clone?

clone方法首先会判对象是否实现了Cloneable接口,若无则抛出CloneNotSupportedException,最后会调用alClone是一个native方法,一般来说native方法的执行效率高于非native方法。当某个类要复写clone方法时,要继承Cloneable接口。通常的克隆对象都是通过()方法来克隆对象。

17. 深克隆和浅克隆?

1、浅克隆:对当前对象进行克隆,并克隆该对象所包含的8种基本数据类型和String类型属性(拷贝一份该对象并重新分配内存,即产生了新的对象);但如果被克隆的对象中包含除8中数据类型和String类型外的其他类型的属性,浅克隆并不会克隆这些属性(即不会为这些属性分配内存,而是引用原来对象中的属性)

2、深克隆:深拷贝会完全复制整个对象,包括这个对象所包含的内部对象。

18. new一个对象的过程和clone一个对象的区别?

一个对象通过new创建的过程为:

1、在内存中开辟一块空间;

2、在开辟的内存空间中创建对象;

3、调用对象的构造函数进行初始化对象。

而一个对象通过clone创建的过程为:

1、根据原对象内存大小开辟一块内存空间;

2、复制已有对象,克隆对象中所有属性值。

相对new来说,clone少了调用构造函数。

需要占用大量连续内存空间的java对象一般称为大对象,比如很长的字符串、数组以及类对象

如果构造函数中存在大量属性初始化或大对象,则使用clone的复制对象的方式性能会好一些。

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

方法的覆盖Overriding 和重载Overloading 是java 多态性的不同表现;覆盖Overriding 是父类与子类之间多态性的一种表现,重载Overloading 是一个类中多态性的一种表现。

4

20. 谈谈你对多态的理解?

一个事物需要不同的状态就可以用多态,譬如建立一个动物类,我有时候需要它是猫,有时候需要是狗。多态的三个特征就是(1)要有继承关系(2)子类重写了父类的方法,静态方法不算(3)父类的引用指向子类的对象

这样建立多态以后,父类可以使用自己的变量以及子类重写了的方法,但是不能使用子类自己的成员变量和方法,如果需要使用得强行转换为子类对象再使用,这样的好处是把子类对象给了子类的引用,避免创建新的子类对象

Animal animal = new Cat();这个属性向上转型

Cat cat = (Cat)animal;这个是向下转型

21. 构造器(constructor)是否可被重写(override)?

构造器不能被继承,因此不能被重写,但可以被重载。

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

不对,如果两个对象 x 和 y 满足 (y) == true,它们的哈希码(hash code)应当相同。Java 对于 eqauls 方法和 hashCode 方法是这样规定的:(1)如果两个对象相同(equals 方法返回 true),那么它们的hashCode 值一定要相同;(2)如果两个对象的 hashCode 相同,它们并不一定相同。当然,你未必要按照要求去做,但是如果你违背了上述原则就会发现在使用容器时,相同的对象可以出现在 Set 集合中,同时增加新元素的效率会大大下降(对于使用哈希存储的系统,如果哈希码频繁的冲突将会造成存取性能急剧下降)。

23. String类的常用方法有哪些?

的构造方法

1)String(String original):把字符串数据封装成字符串对象。

2)String(char[] value):把字符数组的数据封装成字符串对象。

3)String(char[] value, int index, int count):把字符数组中的一部分数据封装成字符串对象。

类的获取功能:

1)length():获取字符串的长度,其实也就是字符个数。

2)charAt(int index):获取指定索引处的字符。

3)indexOf(String str):获取str在字符串对象中第一次出现的索引。

4)substring(int start):从start开始截取字符串。

5)String substring(int start,int end):从start开始,到end结束截取字符串。包括start,不包括end。

判断功能

1)equals(Object obj):比较字符串的内容是否相同。

2)equalsIgnoreCase(String anotherString):比较字符串的内容是否相同,忽略大小写。

3)startsWith(String prefix):判断字符串对象是否以指定的字符开头(区分大小写)。

4)startsWith(String prefix,int toffset):判断字符串对象是否以指定的字符开头,参数toffset为指定从哪个下标开始。

5)endsWith(String str):判断字符串对象是否以指定的字符结尾。

6)isEmpty():判断指定字符串是否为空。

5

7)compareTo(String anotherString):比较字符串的大小,前者大返回整数,后者大返回负数,相等返回0。

类中的转化方法:

1)toCharArray():把字符串转换为字符数组。

2)toLowerCase():把字符串转换为小写字符串。

3)toUpperCase():把字符串转换为大写字符串。

5.其他常用方法

1)trim():去除字符串两端空格。

2)split():去除字符串中指定的的字符,然后返回一个新的字符串。

3)subSequence(int beginIndex,int endIndex ):截取字符串中指定位置的字符组成一个新的字符串。

4)replace(char oldChar, char newChar):将指定字符替换成另一个指定的字符。

5)replaceAll(String regex,String replasement):用新的内容替换全部旧内容。

6)replaceFirst(String regex,String replacement):替换首个满足条件的内容。

7)lastIndexOf(String str):返回指定字符出现的最后一次的下标。

8)contains(CharSequence s):查看字符串中是都含有指定字符。

9)concat(String str):在原有的字符串的基础上加上指定字符串。

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

char类型可以存储一个中文汉字,因为Java中使用的编码是Unicode(不选择任何特定的编码,直接使用字符在字符集中的编号,这是统一的唯一方法),一个char类型占2个字节(16bit),所以放一个中文是没问题的。

补充:使用Unicode意味着字符在JVM内部和外部有不同的表现形式,在JVM内部都是Unicode,当这个字符被从JVM内部转移到外部时(例如存入文件系统中),需要进行编码转换。所以Java中有字节流和字符流,以及在字符流和字节流之间进行转换的转换流,如InputStreamReader和OutputStreamReader,这两个类是字节流和字符流之间的适配器类,承担了编码转换的任务;对于C程序员来说,要完成这样的编码转换恐怕要依赖于union(联合体/共用体)共享内存的特征来实现了。

25. this关键字的用法

一、this关键字主要有三个应用:

(1)this调用本类中的属性,也就是类中的成员变量。

(2)this调用本类中的其他方法。

(3)this调用本类中的其他构造方法,调用时要放在构造方法的首行。

Public Class Student {

String name; //定义一个成员变量name

private void SetName(String name) { //定义一个参数(局部变量)name

=name; //将局部变量的值传递给成员变量

}

}

应用一:引用成员变量

如上面这段代码中,有一个成员变量name,同时在方法中有一个形式参数,名字也是name,然后在方法中将形式参数name的值传递给成员变量name,虽然我们可以看明白这个代码的含义,但是作为Java编译器它是怎么判断的呢?到底是将形式参数name的值传递给6

成员变量name,还是反过来将成员变量name的值传递给形式参数name呢?也就是说,两个变量名字如果相同的话,那么Java如何判断使用哪个变量?此时this这个关键字就起到作用了。this这个关键字其代表的就是对象中的成员变量或者方法。也就是说,如果在某个变量前面加上一个this关键字,其指的就是这个对象的成员变量或者方法,而不是指成员方法的形式参数或者局部变量。为此在上面这个代码中,代表的就是对象中的成员变量,又叫做对象的属性,而后面的name则是方法的形式参数,代码=name就是将形式参数的值传递给成员变量。这就是上面这个代码的具体含义。

一般情况下,在Java语言中引用成员变量或者成员方法都是以对象名.成员变量或者对象名.成员方法的形式。不过有些程序员即使在没有相同变量的时候,也喜欢使用this.成员变量的形式来引用变量,这主要是从便于代码的阅读考虑的。一看到这个this关键字就知道现在引用的变量是成员变量或者成员方法,而不是局部变量。这无形中就提高了代码的阅读性。不过话说回来,这是this关键字在Java语言中的最简单的应用。从这个应用中,我们可以看出this关键字其代表的就是对象的名字。

其实如果是局部变量的话,也是相同的道理。如在上面的代码中,name不是形式参数,而是一个局部变量。此时Java也会遇到相同的疑惑,即变量名name代表的到底是局部变量还是形式参数?name=name到底代表的是什么含义?根据局部变量的作用域,在方法内部,如果局部变量与成员变量同名的话,那么是以局部变量为准。可是在name=name这个赋值语句中,将局部变量的值赋值给自己,显然并不是很合适。根据代码的含义,本来的意思应该是将局部变量赋值给成员变量。为了更清晰的表达这个含义,为此最好采用如下的书写格式=name。这里的this关键字含义就是对象名student,为此就表示。

应用二:调用类的构造方法

public class Student { //定义一个类,类的名字为student。

public Student() { //定义一个方法,名字与类相同故为构造方法

this(“Hello!”);

}

public Student(String name) { //定义一个带形式参数的构造方法

}

}

this关键字除了可以调用成员变量之外,还可以调用构造方法。在一个Java类中,其方法可以分为成员方法和构造方法两种。构造方法是一个与类同名的方法,在Java类中必须存在一个构造方法。如果在代码中没有显示的体现构造方法的话,那么编译器在编译的时候会自动添加一个没有形式参数的构造方法。这个构造方法跟普通的成员方法还是有很多不同的地方。如构造方法一律是没有返回值的,而且也不用void关键字来说明这个构造方法没有返回值。而普通的方法可以有返回值、也可以没有返回值,程序员可以根据自己的需要来定义。不过如果普通的方法没有返回值的话,那么一定要在方法定义的时候采用void关键字来进行说明。其次构造方法的名字有严格的要求,即必须与类的名字相同。也就是说,Java编译器发现有个方法与类的名字相同才把其当作构造方法来对待。而对于普通方法的话,则要求不能够与类的名字相同,而且多个成员方法不能够采用相同的名字。在一个类中可以存在多个构造方法,这些构造方法都采用相同的名字,只是形式参数不同。Java语言就凭形式参数不同来判断调用那个构造方法。

在上面这段代码中,定义了两个构造方法,一个带参数,另一个没有带参数。构造方法都不会有返回值,不过由于构造方法的特殊性,为此不必要在构造方法定义时带上void关键字来说明这个问题。在第一个没有带参数的构造方法中,使用了this(“Hello!”)这7

句代码,这句代码表示什么含义呢?在构造方法中使this关键字表示调用类中的构造方法。如果一个类中有多个构造方法,因为其名字都相同,跟类名一致,那么这个this到底是调用哪个构造方法呢?其实,这跟采用其他方法引用构造方法一样,都是通过形式参数来调用构造方法的。如上例中,this关键字后面加上了一个参数,那么就表示其引用的是带参数的构造方法。如果现在有三个构造方法,分别为不带参数、带一个参数、带两个参数。那么Java编译器会根据所传递的参数数量的不同,来判断该调用哪个构造方法。从上面示例中可以看出,this关键字不仅可以用来引用成员变量,而且还可以用来引用构造方法。

不过如果要使用这种方式来调用构造方法的话,有一个语法上的限制。一般来说,利用this关键字来调用构造方法,只有在无参数构造方法中第一句使用this调用有参数的构造方法。否则的话,翻译的时候,就会有错误信息。这跟引用成员变量不同。如果引用成员变量的话,this关键字是没有位置上的限制的。如果不熟悉这个限制的话,那么还是老老实实的采用传统的构造方法调用方式为好。虽然比较麻烦,但是至少不会出错。

应用三:返回对象的值

this关键字除了可以引用变量或者成员方法之外,还有一个重大的作用就是返回类的引用。如在代码中,可以使用return this,来返回某个类的引用。此时这个this关键字就代表类的名称。如代码在上面student类中,那么代码代表的含义就是return student。可见,这个this关键字除了可以引用变量或者成员方法之外,还可以作为类的返回值,这才是this关键字最引人注意的地方。

26. super关键字的用法

(1)指向父类对象。

(2)调用父类的方法。

(3)super() 可以调用父类的构造方法。

27. this与super的区别

使用方法

如果子类中出现非私有的同名变量,要引用时:

(1) 访问父类中的同名变量,用super。

(2) 访问子类本类中的变量,用this。

如果子类重写方法时,要引用父类中的已经定义好的功能时,用super.方法。

如果子类中的构造函数需要指定父类中其他构造函数进行初始化,使用super(参数)的形式。

如果子类中的构造函数中,需要引用父类中已经定义好的方法,使用super(参数)

主要区别

super的使用基本与this一致,主要区别在于

super代表父类对象的引用

this代表本类对象的引用

28. static存在的主要意义

1.创建独立于具体对象的域变量或者方法,以至于即使没有创建对象,也能使用属性和调用方法。

2.形成静态代码以优化程序性能,类初次加载时,会按照static的顺序来执行每个static块,并且只会执行一次。因此将只需要进行一次的初始化操作都放在static代码块中进行。

8

29. static的独特之处

1.被static修饰的变量或者方法是独立于类中的任何对象,这些变量和方法不属于任何一个实例对对象,而是被类的实例对象共享。

2.在该类被第一次加载的时候,static修饰的段就会被加载,而且只在类第一次使用时加载并进行初始化,后面可以再次赋值。

变量在类加载时分配空间,之后不再重新分配,但是可以任意赋值。

4.被static修饰的变量或方法是优先于对象存在的,就是说当一个类加载完成,即使没有创建对象也可以访问。

30. static应用场景

因为static是被类的实例对象共享,因此某个成员变量是被所有对象共享的,那么这个成员变量就定义为静态常量。

因此常见的static修饰有:

1.修饰成员变量

2.修饰成员方法

3.修饰代码块

4.修饰类

5.静态导包

31. static注意事项

1.静态只能访问静态。

2.非静态既能访问非静态,也可以访问静态。

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

一、作用不同

1、break:执行break操作,跳出所在的当前整个循环,到外层代码继续执行。

2、continue:执行continue操作,跳出本次循环,从下一个迭代继续运行循环,内层循环执行完毕,外层代码继续运行。

3、return:执行return操作,直接返回函数,所有该函数体内的代码(包括循环体)都不会再执行。

二、结束不同

1、break:break不仅可以结束其所在的循环,还可结束其外层循环,但一次只能结束一种循环。

2、continue:continue结束的是本次循环,将接着开始下一次循环。

3、return:return同时结束其所在的循环和其外层循环。

33. 在Java中定义一个不做事且没有参数的构造方法的作用

Java在执行子类的构造方法时,假如没有用super()来调用父类特定的构造方法,则会默认调用父类中没有参数的构造方法。因此,假如父类中只定义了带参数的构造方法,而在子类的构造方法中没有调用又没有用super()来调用父类中特定的构造方法,则编译器将发生错误。因为Java程序在父类中没有找到无参数的构造方法可供执行。解决办法就是在父类里加上一个不做事且没有参数的构造方法。

34. 构造方法有哪些特性?

9

1.名字与类名相同。

2.没有返回值,但不能用 void 声明构造函数。

3.生成类的对象时自动执行,无需调用。

若一个类没有声明构造方法,该程序能正确执行吗

构造方法主要作用是完成对类对象的初始化工作。可以执行。因为一个类即使没有声明构造方法也会有默认的无参构造方法。

35. 静态变量和实例变量区别

1.静态变量 static关键字修饰,为共享的,别人调用完后值是改变的。

2.实例变量 也称非静态变量,不是共享的,每个对象都有一套实例变量,自己用自己的,不受别人对象的影响。

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

1、静态方法属于整个类所有,因此调用它不需要实例化,可以直接调用(类.静态方法())。实例方法必须先实例化,创建一个对象,才能进行调用(对象.实例方法())。

2、静态方法只能访问静态成员,不能访问实例成员;而实例方法可以访问静态成员和实例成员。

3、在程序运行期间,静态方法是一直存放在内存中,因此调用速度快,但是却占用内存。实例方法是使用完成后由回收机制自动进行回收,下次再使用必须再实例化。

4、一般来说,公共的函数、经常调用的可以写成静态方法,比如数据连接等(SqlHelper)。

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

方法的返回值是某个方法体中的代码执行后产生的结果。

返回值的作用是接受结果,使得它可以用于其它操作。

38. 什么是内部类?

内部类(Inner Class),是Java中对类的一种定义方式,是嵌套类的一个分类,即非静态嵌套类(Non-Static Nested Class)。内部类(非静态嵌套类)分为成员内部类、局部内部类和匿名内部类三种。

39. 内部类的分类有哪些

内部类可以分为四种: 成员内部类、局部内部类、匿名内部类和静态内部类 。

40. Java中异常分为哪些种类?

Throwable包含了错误(Error)和异常(Excetion两类),Exception又包含了运行时异常(RuntimeException, 又叫非检查异常)和非运行时异常(又叫检查异常)。

检查型异常:检查型异常需要程序员提供处理方式,如捕获或者抛出。除了Error和RuntimeException及其子类,其他的异常都是检查型异常。

错误:错误是由程序以外的因素引起的,通常不需要程序员关心。Error及其所有子类都是错误。

运行时异常:运行时异常是由程序员失误引起的,这些异常信息都可以避免。RuntimeException及其所有子类都是运行时异常。

41. hashCode 与 equals (重要)

10

HashSet如何检查重复两个对象的hashCode()相同,则equals()也一定为 true,对吗?

hashCode和equals方法的关系面试官可能会问你:“你重写过 hashcode 和 equals

么,为什么重写equals时必须重写 hashCode方法?”

42. hashCode()介绍

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

散列表存储的是键值对(key-value),它的特点是:能根据“键”快速的检索出对应的“值”。这其中就利用到了散列码。

43. 为什么要有 hashCode

我们先以“HashSet 如何检查重复”为例子来说明为什么要有 hashCode: 当你把对象加入 HashSet时,HashSet 会先计算对象的 hashcode 值来判断对象加入的位置,同时也会与该位置其他已经加入的对象的 hashcode 值作比较,如果没有相符的 hashcode,HashSet 会假设对象没有重复出现。但是如果发现有相同 hashcode 值的对象,这时会调用 equals() 方法来检查 hashcode 相等的对象是否真的相同。如果两者相同,HashSet 就不会让其加入操作成功。如果不同的话,就会重新散列到其他位置。这样我们就大大减少了 equals 的次数,相应就大大提高了执行速度。

由此可以看出: hashCode() 的作用就是获取哈希码,也称为散列码;它实际上是返回一个 int 整数。这个哈希码的作用是确定该对象在哈希表中的索引位置。 hashCode() 在散列表中才有用,在其它情况下没用。在散列表中 hashCode() 的作用是获取对象的散列码,进而确定该对象在散列表中的位置。

hashCode()与 equals()的相关规定:

如果两个对象相等,则 hashcode 一定也是相同的

两个对象相等,对两个对象分别调用 equals 方法都返回 true

两个对象有相同的 hashcode 值,它们也不一定是相等的

因此,equals 方法被覆盖过,则 hashCode 方法也必须被覆盖

hashCode() 的默认行为是对堆上的对象产生独特值。

44. 抽象类和接口(Java7)的区别

1. 抽象类通过 abstract 关键字来定义,然后子类通过 extends 继承该抽象类后并实现相应抽象方法;接口通过 interface 关键字来定义,子类通过 implements 来实现该接口中的所有方法。

2. 抽象类中的抽象方法可以使用 public、protected、default 修饰符;接口中的抽象方法默认并只能是 public,并且成员变量默认为 public static final 修饰的,所以我们可以直接通过 接口名.成员变量 使用它。

3. 抽象类中允许有非抽象的方法和成员变量包括构造方法; 接口中的方法全是抽象的,不能有方法的实现。

4. 子类只能通过继承来实现抽象类,由于 java 中的单继承特性,就导致只能继承一个抽象类;但是子类可以实现一个或多个接口,在一定程度上,这就解决了由于单继承特性所带来的问题。

5. 从作用上来看,抽象类是为了把相同的东西提取出来,即重用;接口是为了把程序进行模块化,可以降低程序的耦合。

11

45. Java 8的接口新增了哪些特性?

1,新特性

接口中增加了静态方法和用default修饰的默认方法,jdk8以前的版本是不支持这两个新特性的。

接口中的静态方法,子类可以直接用接口名.方法名访问,且不能被重写(静态方法可以被继承,但是不能被重写)

接口汇总的default修饰的默认方法,可以被子类继承且被子类重写。

2,原因

为什么java8新增了这两个特性,是因为如果一个已经使用了很久的接口,且有多个子类继承了它,现在需要加一个新的方法,可是有的子类却不需要他,有的需要,这个时候增加静态方法和default修饰的默认方法就可以达到这个目的。需要的可以直接使用或者重写。

46. 重写和重载的区别

1、定义不同:重载是定义相同的方法名、参数不同,重写是子类重写父类的方法

2、范围不同:重载是在一个类中,重写是子类与父类之间的

3、多态不同:重载是编译时的多态性,重写是运行时的多态性

4、参数不同:重载的参数个数、参数类型、参数的顺序可以不同,重写父类子方法参数必须相同

5、修饰不同:重载对修饰范围没有要求,重写要求重写方法的修饰范围大于被重写方法的修饰范围

多态是一个类需要表现出多种形态,子类重写父类的方法,使子类具有不同的方法实现

47. ArrayList和LinkedList有什么区别?

ArrayList 和 LinkedList 都是List接口的子类,间接实现Collection接口

Collection接口处理单列数据的接口,自然ArrayList 和 LinkedList都是处理单列数据的类

ArrayList:

1.动态数据的存储结构

2.添加/删除数据慢,查询数据快

3.没有提供对第一个和最后一个元素的操作方法

4.有10个单位的初始容量

5.扩展机制是原来的1.5倍

LinkedList:

1.双向链表的存储结构

2.添加/删除快,查询数据慢

3.提供对第一个和最后一个元素的操作方法addlast()........

4.没有初始容量

5.没有扩展机制

48. HashMap是怎么实现的?

1、hashmap实现原理

HashMap是基于hashing的原理,我们使用put(key, value)存储对象到HashMap中,使用get(key)从HashMap中获取对象。

12

当我们给put()方法传递键和值时,我们先对键调用hashCode()方法,返回的hashCode用于找到bucket位置来储存Entry对象。

2、(key,value)实现原理

(1)首先将k,v封装到Node对象当中(节点)。

(2)然后它的底层会调用K的hashCode()方法得出hash值。

(3)通过哈希表函数/哈希算法,将hash值转换成数组的下标,下标位置上如果没有任何元素,就把Node添加到这个位置上。如果说下标对应的位置上有链表。此时,就会拿着k和链表上每个节点的k进行equal。如果所有的equals方法返回都是false,那么这个新的节点将被添加到链表的末尾。如其中有一个equals返回了true,那么这个节点的value将会被覆盖。

3、(key)实现原理

(1)先调用k的hashCode()方法得出哈希值,并通过哈希算法转换成数组的下标。

(2)通过上一步哈希算法转换成数组的下标之后,在通过数组下标快速定位到某个位置上。如果这个位置上什么都没有,则返回null。如果这个位置上有单向链表,那么它就会拿着K和单向链表上的每一个节点的K进行equals,如果所有equals方法都返回false,则get方法返回null。如果其中一个节点的K和参数K进行equals返回true,那么此时该节点的value就是我们要找的value了,get方法最终返回这个要找的value。

49. HashMap在Java7和Java8中的实现有什么不同?

HashMap 在 JDK 7 和 JDK 8 的主要区别如下。

存储结构:JDK 7 使用的是数组 + 链表;JDK 8 使用的是数组 + 链表 + 红黑树。

存放数据的规则:JDK 7 无冲突时,存放数组;冲突时,存放链表;JDK 8 在没有冲突的情况下直接存放数组,有冲突时,当链表长度小于 8 时,存放在单链表结构中,当链表长度大于 8 时,树化并存放至红黑树的数据结构中。

插入数据方式:JDK 7 使用的是头插法(先将原位置的数据移到后 1 位,再插入数据到该位置);JDK 8 使用的是尾插法(直接插入到链表尾部/红黑树)。

50. HashMap有时候会死循环,你知道是什么原因吗?

在了解来龙去脉之前,我们先看看HashMap的数据结构。

在内部,HashMap使用一个Entry数组保存key、value数据,当一对key、value被加入时,会通过一个hash算法得到数组的下标index,算法很简单,根据key的hash值,对数组的大小取模 hash & (length-1),并把结果插入数组该位置,如果该位置上已经有元素了,就说明存在hash冲突,这样会在index位置生成链表。

如果存在hash冲突,最惨的情况,就是所有元素都定位到同一个位置,形成一个长长的链表,这样get一个值时,最坏情况需要遍历所有节点,性能变成了O(n),所以元素的hash值算法和HashMap的初始化大小很重要。

当插入一个新的节点时,如果不存在相同的key,则会判断当前内部元素是否已经达到阈值(默认是数组大小的0.75),如果已经达到阈值,会对数组进行扩容,也会对链表中的元素进行rehash。

51. ConcurrentHashMap是怎么实现的?

在JDK8中,ConcurrentHashMap的底层数据结构与HashMap一样,也是采用“数组+链表+红黑树”的形式。同时,它又采用锁定头节点的方式降低了锁粒度,以较低的性能代价实现了线程安全。底层数据结构的逻辑可以参考HashMap的实现,下面我重点介绍它的线程13

安全的实现机制。

初始化数组或头节点时,ConcurrentHashMap并没有加锁,而是CAS的方式进行原子替换(原子操作,基于Unsafe类的原子操作API)。

插入数据时会进行加锁处理,但锁定的不是整个数组,而是槽中的头节点。所以,ConcurrentHashMap中锁的粒度是槽,而不是整个数组,并发的性能很好。

扩容时会进行加锁处理,锁定的仍然是头节点。并且,支持多个线程同时对数组扩容,提高并发能力。每个线程需先以CAS操作抢任务,争抢一段连续槽位的数据转移权。抢到任务后,该线程会锁定槽内的头节点,然后将链表或树中的数据迁移到新的数组里。

查找数据时并不会加锁,所以性能很好。另外,在扩容的过程中,依然可以支持查找操作。如果某个槽还未进行迁移,则直接可以从旧数组里找到数据。如果某个槽已经迁移完毕,但是整个扩容还没结束,则扩容线程会创建一个转发节点存入旧数组,届时查找线程根据转发节点的提示,从新数组中找到目标数据。

52. 静态代理和动态代理的区别

静态代理和动态代理的区别

1、静态代理通常只代理一个类,动态代理是代理一个接口下的多个实现类。

2、静态代理事先知道要代理的是什么,而动态代理不知道要代理什么东西,只有在运行时才知道。

3、动态代理是实现JDK里的InvocationHandler接口的invoke方法,但注意的是代理的是接口,也就是你的业务类必须要实现接口,通过Proxy里的newProxyInstance得到代理对象。

4、还有一种动态代理CGLIB,代理的是类,不需要业务类继承接口,通过派生的子类来实现代理。通过在运行时,动态修改字节码达到修改类的目的。

静态代理

某个对象提供一个代理,代理角色固定,以控制对这个对象的访问。 代理类和委托类有共同的父类或父接口,这样在任何使用委托类对象的地方都可以用代理对象替代。代理类负责请求的预处理、过滤、将请求分派给委托类处理、以及委托类执行完请求后的后续处理。

静态代理的特点

1、目标角色固定

2、在应用程序执行前就得到目标角色

3、代理对象会增强目标对象的行为

4、有可能存在多个代理 引起"类爆炸"(缺点)

动态代理

相比于静态代理,动态代理在创建代理对象上更加的灵活,动态代理类的字节码在程序运行时,由Java反射机制动态产生。它会根据需要,通过反射机制在程序运行期,动态的为目标对象创建代理对象,无需程序员手动编写它的源代码。

动态代理的特点

1、目标对象不固定

2、在应用程序执行时动态创建目标对象

3、代理对象会增强目标对象的行为

53. JDK动态代理和CGLIB动态代理的区别

1、JDK和CGLIB动态代理的区别

JDK代理使用的是反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用I14

nvokeHandler来处理。

CGLIB代理使用字节码处理框架asm,对代理对象类的class文件加载进来,通过修改字节码生成子类。

JDK创建代理对象效率较高,执行效率较低;CGLIB创建代理对象效率较低,执行效率高。JDK动态代理机制是委托机制,只能对实现接口的类生成代理,通过反射动态实现接口类; CGLIB则使用的继承机制,针对类实现代理,被代理类和代理类是继承关系,所以代理类是可以赋值给被代理类的,因为是继承机制,不能代理final修饰的类。

JDK代理是不需要依赖第三方的库,只要JDK环境就可以进行代理,需要满足以下要求:1.实现InvocationHandler接口,重写invoke()。

2.使用xyInstance()产生代理对象。

3.被代理的对象必须要实现接口。

CGLib 必须依赖于CGLib的类库,需要满足以下要求:

1.实现MethodInterceptor接口,重写intercept()。

2.使用Enhancer对象.create()产生代理对象。

做一个总结

代理只能对实现了接口的类进行代理,而cglib代理可以对普通类进行代理;

代理是通过反射的方式来实现动态代理,而cglib则是通过为目标类生成一个子类的方式来实现动态代理;

c.由于cglib代理是为目标类生成了一个子类,并对父类方法进行增强,所以目标类不能用final修饰;

2、使用JDK还是CGLIB

1)如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP,可以强制使用CGLIB实现AOP

2)如果目标对象没有实现了接口,必须采用CGLIB库,spring会自动在JDK动态代理和CGLIB之间转换

3、强制使用CGLIB实现AOP的方法

1)添加CGLIB库(、、)

2)在Spring配置文件中加入

15


本文标签: 对象 方法 代理 变量