JAVA面试部分重点内容
目录
- JAVA面试部分重点内容
- 一、Java基础
- 1.为什么java代码可以实现一次编写,到处运行?
- 2.一个java文件里可以有多个类吗(不含内部类)?
- 3.对java访问权限的理解?
- 4.int类型的数据范围是多少?
- 5.成员变量和局部变量的区别?
- 6.为啥有包装类?
- 7.说说对面向对象的理解?
- 8.说说对this关键字的理解?
- 9.说说重写与重载的区别?
- 10.说说你对多态的理解?
- 11.说说对static关键字的理解?
- 12.说说static和final的区别?
- 13.说说hashCode()与equals()的关系?
- 14.说说==与equals()有什么区别?
- 15.说一说String和StringBuffer的区别?
- 16.接口和抽象类有什么区别?
- 17.遇到过异常吗,如何处理?
- 18.说说你对泛型的理解?
- 19.说说对泛型类的继承关系的理解?
- 20.说说对Java反射机制的理解?
- 总结
一、Java基础
1.为什么java代码可以实现一次编写,到处运行?
JVM是java跨平台的关键。程序运行前,java源代码(.java)需要经过编译器编译成字节码(.class)。程序运行时,JVM负责将字节码翻译成特定平台下的机器码并运行,即在不同平台上安装对应的JVM,就可以运行字节码文件。
2.一个java文件里可以有多个类吗(不含内部类)?
- 一个Java文件里可以有多个类,但最多只能有一个被public修饰的类;
- public修饰的类的名称必须和java文件名一致
3.对java访问权限的理解?
在修饰成员变量/成员方法:
private | 该类内部成员访问 |
---|---|
defalut | 该类内部成员访问,被同一包下其他类访问 |
protected | 该类内部成员访问,被同一包下其他类访问,它的子类访问 |
public | 任意包下,任意类的成员访问 |
在修饰类时,该类只有两种访问权限:
defalut | 该类被同一包下的其他类访问 |
---|---|
public | 任意包下,任意类访问 |
4.int类型的数据范围是多少?
-231~231-1
5.成员变量和局部变量的区别?
成员变量 | 局部变量 |
---|---|
在类的范围里定义的变量 | 在方法里定义的变量 |
有默认初始值 | 没有默认初始值 |
未被static修饰的成员变量叫实例变量,存储于对象所在的堆内存,生命周期与对象相同;被static修饰的成员变量也叫类变量,存储于方法区中,生命周期与当前类相同 | 存储于栈内存中,作用的范围结束,变量空间会自动释放 |
6.为啥有包装类?
java并不是纯面向对象的语言,java语言是一个面向对象的语言,但是java中的基本数据类型却不是面向对象的,但是我们在实际使用中经常将基本数据类型转换成对象,便于操作,比如,集合的操作中,这时,我们就需要将基本类型数据转化成对象。
包装类的继承关系
- 自动装箱:可以把一个基本类型的数据直接赋给对应的包装类型
- 自动拆箱:可以把一个包装类型的对象直接赋给对应的基本类型
7.说说对面向对象的理解?
面向对象是向现实世界模型的自然延伸,这是一种“万物皆对象”的编程思想。在现实生活中的任何物体都可以归为一类事物,而每一个个体都是一类事物的实例。面向对象的编程是以对象为中心,以消息为驱动,所以程序=对象+消息。面向对象有三大特性,封装、继承和多态。
- 封装:将对象的实现细节隐藏起来,通过一些公用方法暴露该对象的功能
- 继承:面向对象实现软件复用的重要手段,当子类继承父类后,子类直接获得父类的属性和方法
- 多态:子类对象可以直接复制给父类变量,运行时表现出子类的行为特征,这意味着同一个类型的对象在执行同一个方法时,可能表现出多种行为特征。
8.说说对this关键字的理解?
-
当前对象的凭证
格式:
a. this.xx (xx代表成员变量)
b. this.xx() (xx代表成员方法)
注意: a中的xx为成员变量, b中的xx为成员方法 -
调用其他的构造方法(只能在构造方法的第一行出现)
格式: this+()
注意:()里面为参数传递! -
什么情况下对成员变量的访问一定需要是使用this?
参数列表和成员变量名冲突的时候
9.说说重写与重载的区别?
- 重载发生在同一个类中,若多个方法之间名相同,参数列表不同,则构成重载关系。重载与方法的返回值以及访问修饰符无关。
- 重写发生在父类子类中,若子类方法想和父类方法构成重写关系,则它的方法名,参数列表必须与父类相同。另外,返回值要小于等于父类方法,抛出的异常要小于等于父类方法,访问修饰符则要大于等于父类方法。还有,若父类方法的访问修饰符为private,则子类不能对其重写。
10.说说你对多态的理解?
向上转型 | 向下转型 |
---|---|
定义:子类对象(小范围)实例化父类对象(大范围),这种属于自动转换 | 定义:通过父类对象(大范围)实例化子类对象(小范围),这种属于强制转换 |
特点:①对于成员方法去父类寻找这个方法(再次强调是通过方法名称和参数列表)a.若这个方法父类不存在,则不能通过编译。b.若这个方法父类存在,则访问的是子类的方法。②对于成员变量a.父类不存在这个成员变量,则不能通过编译b.仅仅能访问到父类的成员变量! | 特点:向下转型则是为了通过父类强制转换为子类,从而来调用子类独有的方法。为了保证向下转型的顺利完成,在java中提供了一个关键字:instanceof,通过instanceof可以判断某对象是否是某类的实例,如果是则返回true,否则为false。举例: a instance of A 即a是否是A类本身或者是A类的子类。 |
-
多态性定义:
多态就是指程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编译时并不确定,而是在程序运行期间才确定,这就是多态性。 -
多态的存在有三个前提:
1.要有继承关系
2.子类要重写父类的方法
3.父类引用指向子类对象
11.说说对static关键字的理解?
在java类只能包括成员变量,方法,构造器,初始化块,内部类(包括接口,枚举),以static修饰的成员就是类成员,类成员属于整个类,而不属于单个对象。
对static关键字而言,有一条重要的规则:类成员(包括成员变量,方法,初始化块,内部类和内部枚举)不能访问实例成员(包括成员变量,方法,初始化块,内部类和内部枚举)。因为类成员是属于类的,类成员的作用域比实例成员作用域更大,完全可能出现类成员已经初始化完成,但实例化成员还不曾初始化的情况。
- 补充知识点:static关键字不能修饰外部类,能修饰内部类
静态内部类需满足的规则:
1.静态内部类可以包含静态成员,也可以包含非静态成员;
2.静态内部类不能访问外部类的实例成员,只能访问它的静态成员;
3.外部类的所有方法,初始化块都能访问其内部定义的静态内部类;
4.在外部类的外部,也可以实例化静态内部类,语法如下:
外部类.内部类 变量名=new 外部类.内部类构造方法():
12.说说static和final的区别?
static关键字可以修饰成员变量、成员方法、初始化块、内部类,被 static修饰的成员是类的成员,它属于类、不属于单个对象。以下是 static修饰这4种成员时表现出的特征:
类变量:被 static修饰的成员变量叫类变量(静态变量)。类变量属于类,它随类的信息存储在方法区,并不随对象存储在堆中,类变量可以通过类名来访问,也可以通过对象名来访问,但建议通过类名访问它。
类方法:被 static修饰的成员方法叫类方法(静态方法)。类方法属于类,可以通过类名访问,也可以通过对象名访问,建议通过类名访问它。
静态块:被 static修饰的初始化块叫静态初始化块。静态块属于类,它在类加载的时候被隐式调用一次,之后便不会被调用了。
静态内部类:被 static修饰的内部类叫静态内部类。静态內部类可以包含静态成员,也可以包含非静态成员。静态内部类不能访问外部类的实例成员,只能访问外部类的静态成员。外部类的所有法、初始化块都能访问其内部定义的静态內部类。
fina关键字可以修饰类、方法、变量,以下是fnal修饰这3种目标时表现出的特征
final类: final关键字修饰的类不可以被继承。
fina方法:fnal关键字修饰的方法不可以被重写。
fina变量:fina关键字修饰的变量,一旦获得了初始值,就不可以被修改。
13.说说hashCode()与equals()的关系?
hashCode()用于获取哈希码(散列码),equals()用于比较两个对象是否相等,它们应遵守如下规定:
- 如果两个对象相等,则它们必须有相同的哈希码。
- 如果两个对象有相同的哈希码,则它们未必相等。
扩展阅读
在Java中,Set接口代表无序的、元素不可重复的集合, HashSet则是Set接口的典型实现,当向 Hash Set中加入一个元素时,它需要判断集合中是否已经包含了这个元素,从而避免重复存储。由于这个判断十分的频繁,所以要讲求效率,绝不能采用遍历集合逐个元素进行比较的方式。实际上HashSet()是通过获取对象的哈希码,以及调用对象的 equals()方法来解决这个判断问题的。HashSet首先会调用对象的hashCode()方法获取其哈希码,并通过哈希码确定该对象在集合中存放的位置。假设这个位置之前已经存了一个对象,则 Hash Set会调用 equals()对两个对象进行比较。若相等则说明对象重复,此时不会保存新加的对象。若不等说明对象不重复,但是它们存储的位置发生了碰撞。此时 HashSet会采用链式结构在同一位置保存多个对象,即将新加对象链接到原来对象的之后。之后再有新添加对象也映射到这个位置时,就需要与这个位置中所有的对象进行 equals()比较,若均不相等,则将其链到最后一个对象之后。
14.说说==与equals()有什么区别?
==运算符:
- 作用于基本数据类型,比较两个数值是否相等;
- 作用于引用数据类型时,比较两个对象的内存地址是否相等,即判断它们是否为一个对象;
equals()方法: - 没有重写时,Object默认以==实现,即比较两个对象的内存地址是否相等;
- 进行重写后,一般会按照对象的内容来进行比较,若两个对象内容相同则认为对象相等,否则认为对象不等。
15.说一说String和StringBuffer的区别?
- String类是不可变类,即一旦一个String对象被创建以后,包含在这个对象中的字符序列是不可改变的,直至这个对象对被销毁。
- StringBuffer对象则代表一个字符序列可变的字符串,当一个StringBuffer被创建以后,通过StringBuffer提供的append(),insert(),reverse()等方法可以改变这个字符串对象的字符序列。一旦通过StringBuffer生成了最终想要的字符串,就可以调用它的toString()方法将其转换为一个String对象。
16.接口和抽象类有什么区别?
从使用方式上来说,二者有如下的区别:
- 接口里只能包含抽象方法、静态方法、默认方法和私有方法,不能为普通方法提供方法实现;抽象类则完全可以包含普通方法。
- 接口里只能定义静态常量,不能定义普通成员变量;抽象类里则既可以定义普通成员变量,也可以定义静态常量。
- 接口里不包含构造器;抽象类里可以包含构造器,抽象类里的构造器并不是用于创建对象,而是让其子类调用这些构造器来完成属于抽象类的初始化操作。
- 接口里不能包含初始化块;但抽象类则完全可以包含初始化块。
- 一个类最多只能有一个直接父类,包括抽象类但个类可以直接实现多个接口,通过实现多个接口可以弥补Java单继承的不足。
17.遇到过异常吗,如何处理?
1.捕获异常
将业务代码包裹在try块内部,当业务代码中发生任何异常时,系统都会为此异常创建一个异常对象。创建异常对象之后,JVM会在try块之后寻找可以处理它的 catch块,并将异常对象交给这个catch块处理。
2.处理异常
在 catch块中处理异常时,应该先记录日志,便于以后追溯这个异常。然后根据异常的类型、结合当前的业务情况,进行相应的处理。比如,给变量赋予一个默认值、直接返回空值、向外抛出一个新的业务异常交给调用者处理,等等。
3.回收资源
如果业务代码打开了某个资源,比如数据库连接、网络连接、磁盘文件等,则需要在这段业务代码执行完毕后关闭这项资源。并且,无论是否发生异常,都要尝试关闭这项资源。将关闭资源的代码写在fnay块内,可以满足这种需求,即无论是否发生异常,fina块内的代码总会被执行。不管try块中的代码是否出现异常,也不管哪一个catch块被执行,甚至在try或catch块中执行了return语句,finally块总会被执行。
-
关于异常跟踪栈
程序运行时,经常会发生一系列方法调用,从而形成方法调用栈。异常机制会导致异常在这些方法之间传播,而异常传播的顺序与方法的调用相反。异常从发生异常的方法向外传播,首先传给该方法的调用者再传给上层调用者,以此类推。最终会传到main()方法,若依然没有得到处理,则JVM会终止程序,并打印异常跟踪栈的信息。 -
Java的异常接口
Throwable是异常的顶层父类,代表所有的非正常情况。它有两个直接子类,分别是Error,EXception。
Error是错误,一般是指与虚拟机相关的问题,如系统崩溃、虛拟机错误、动态链接失败等,这种错误无法恢复或不可能捕获,将导致应用程序中断。通常应用程序无法处理这些错误,因此应用程序不应该试图使用catch块来捕获 Error对象。在定义方法时,也无须在其 throws子句中声明该方法可能抛岀Error及其任何子类。
EXception是异常,它被分为两大类,分别是 Checked异常和 Runtime异常。所有的 Runtime Exception类及其子类的实例被称为 Runtime异常;不是 Runtime Exception类及其子类的异常实例则被称为Checked异常。Java认为 Checked异常都是可以被处理(修复)的异常,所以Java程序必须显式处理Checked异常。如果程序没有处理 Checked异常,该程序在编译时就会发生错误,无法通过编译。Runtime异常则更加灵活,,Runtime异常无须显式声明抛出,如果程序需要捕获 Runtime异常,也可以使用try,catch块来实现。
18.说说你对泛型的理解?
1.在实例化的过程中指定泛型参数
2.指定完泛型参数后,如果传入其他类型的参数编译就会报错
3.方法在返回的时候不需要向下转型
4.如果实例化过程中没有指定泛型参数,则泛型参数相当于Object
class Person<T>{
public T test(T o){
}
}
那我们可以这么调用:
Person<String> p=new Person<String>();
String o1=p.test(“asdasdasd”);
System.out.println(o1.charAt(2));
如果我们希望并不是实例化过程告诉我们泛型参数,而是 方法调用过程 传的参数 告诉我们 返回值得具体类型,从而避免向下转型,则可以使用泛型方法:
<E> E test(E t){
}
泛型方法使用场景:
1.静态方法并不能使用类的泛型参数,因为不需要实例化过程
2.如果这个方法比较多变,则尽量使用泛型方法
19.说说对泛型类的继承关系的理解?
泛型类并不是协变的。意思是说,泛型类的参数具有继承关系,但是泛型类本身并没有任何关系。
需求:
写一个方法 参数列表 可以接受的参数为ArrayList,并且对ArrayList中的泛型参数有一定要----例如是Person+Person的子类。(Student类是继承Person类的)
void test(ArrayList<? extends Person> o){
①
}
那如果是要求泛型参数是是某个类+某个类的父类呢?
void test(ArrayList<? super Person> o){
②
}
对于①来说,我们基本上什么–都不能add,只能add null。
对于②来说,我们可以add Person或者Person的子类。
由于数组是协变的,泛型的类型擦除的影响,导致禁止生成具有泛型类类型数组。
Pair<Double> cell=new Pair<>();
cell.setValue(4.5);
Pair<String>[] arr1=new Pairt<>[10];
Object[] arr2=arr1;
arr2[0]=cell;
String s=arr1[0].getValue();
class Pair<T> {
private T value;
public T getValue() {
return value;
}
public void setValue(T value) {
this.value = value;
}
}
20.说说对Java反射机制的理解?
-
当我们写的.java 文件经过编译后,会生成包含若干个.class 文件。
-
每个 class 文件是一个二进制文件,包含这个类的一些信息。例如有几个成员变量,几个成员方法等。
-
当我们程序运行期间,需要根据实例化某个类的对象时,会将硬盘中的.class 文件在内存中初始化成 Class 类型的对象。
-
例如反射实例化,或者反射拿到一些特定的成员方法,变量等。eg: 类—>.class—>Class 类型的对象—>实例化类对应的对象。
Class 对象的获取方法
- 静态成分
- Class.forName()方法
- getClass()方法
Java反射在实际项目中有哪些应用场景?
- 使用JDBC时,如果要创建数据库的连接,则需要先通过反射机制加载数据库的驱动程序
- 多数框架都支持注解XML配置,从配置中解析出来的类是字符串,需要利用反射机制实例化
- 面向切面编程(AOP)的实现方案,是在程序运行时创建目标对象的代理类,这必须由反射机制来实现。
- 通过反射实现绕过编译