Java基础面试题
1. &和&&区别和联系,|和||区别和联系;实际项目中,什么样情况用哪种
&和&&是Java中的逻辑与运算符,都是双目运算符。如果两个操作数都是true,结果是true,否则是假;无论使用哪个运算符,对最终的运算结果时候没有影响的。
(1)、&称为逻辑与运算符,&&称为短路与运算符。
对于&:无论任何情况,&两边的操作数都会参与计算。 7>8 & 7==7
对于&&:当&&左边的操作数为false时,&&右边的操作数将不参与计算,此时最终结果都为false。
(2)&还可以用作位运算符。当&两边操作数不是boolean类型时,而是整数类型时&用于按位与运算符的操作。但是&&没有这个位运算功能 5 & 6
总:
- 作为逻辑运算符无论使用哪个运算符,对最终的运算结果时候没有影响的。
- 推荐平时多使用&&,因为它效率更高些。
- |和||的联系和区别与&和&&类似,此处不再重复。
2. switch条件变量的取值类型
switch是Java中的多重分支语句,只能进行等值判断,不能进行区间判断。其中条件变量的取值类型是该语句的一个需要重点掌握的内容。
条件变量的取值范围是
- 整数类型中的 byte、short、int。(long不可选)
- 字符类型中的char(可以理解为int)
- JDK1.5后增加了enum枚举类型
- JDK1.7后增加了String类型
不可以是
- 整数类型long (整数中就它不可以)
- Boolean类型(多重分支,boolean只有两个值,就比凑热闹了)
- 浮点类型float、double(不可以精确比较)
可选:
switch中用到了break关键字,用来跳出switch语句;另外break还可以使用在循环语句中。
switch语句中还使用了default关键字,default关键字中还可以使用在接口中,用来表示在JDK8之后接口中定义的非抽象但是可以被实现类重写的方法。
3. 方法重载和方法重写的区别;举例说明项目中怎样用的
1. 作用不同
| | 英文 | 位置不同 | 作用不同 |
|---|---|---|---|---|
| 重载 | overload | 同一个类中 | 在一个类里面为一种行为提供多种实现方式并提高可读性 |
|重写| override | 子类和父类中 | 父类方法无法满足子类的要求,子类通过方法重写满足要求 |
2. 细节区别:一个方法的声明自左向右包括权限修饰符、方法返回值、方法名、参数列表、抛出的异常类型等
| | 修饰符 | 返回值 | 方法名 | 参数 | 抛出异常 |
|---|---|---|---|---|---|---|
| 重载 | 无关 | 无关 | 相同 | 不同 | 无关 |
|重写| 大于等于 | 小于等于 | 相同 | 相同 | 小于等于 |
重载实例:构造方法重载、println()方法重载
重写实例:Object类的toString()、equals()、hashCode()等都可以被子类重写
扩展:
- 某些方法使用final修饰,将无法被重写。比如Object类的wait()、notify()等
- 静态方法无法进行方法重写。在Java中,如果父类中含有一个静态方法,且在子类中也含有一个返回类型、方法名、参数列表均与之相同的静态方法,那么该子类实际上只是将父类中的该同名方法进行了隐藏,而非重写。添加@override注解将会报错。
4. 类和对象
类和对象是面向对象编程技术中的最基本的概念。
类与对象的定义
类是现实世界或思维世界中的实体在计算机中的反映,它将数据以及这些数据上的操作封装在一起。对象是具有类类型的变量。类和对象是面向对象编程技术中的最基本的概念。
类与对象的关系
类是对象的抽象,而对象是类的具体实例。类是抽象的,不占用内存,而对象是具体的,占用存储空间。类是用于创建对象的蓝图,它是一个定义包括在特定类型的对象中的方法和变量的软件模板
生活案例:人就是一个概念,人具有身高,体重等属性;人可以做吃饭、说话等方法。小明就是一个具体的人,也就是实例,他的属性是具体的身高200cm,体重180kg,他做的方法是具体的吃了一碗白米饭,说了“12345”这样一句话。
5. private/默认/protected/public权限修饰符的区别
这是Java中的四个权限修饰符,和面向对象的三大特征的封装性有密切关系。这四个修饰符都可以修饰类的成员,另外其中的默认和public还可以修饰类
| 访问控制 | public | protected | 默认 | private |
|---|---|---|---|---|---|---|
| 同一个类中 无关 | ✅ | ✅ | ✅ | ✅ |
| 同一包中其它类 | ✅ | ✅ | ✅ | |
| 不同包中的子类 | ✅ | ✅ | | |
| 不同包中非子类 | ✅ | | | |
其中默认和public权限可以修饰类
public:公共的 可被同一项目中所有包的所有类访问。 (须与文件名同名)
默认的:可被同一个包中的类访问。
-
内部类属于类的成员,可以使用四个修饰符修饰,这一点和类不同。
-
如何使用:类的属性一般使用private修饰,可以通过public的setter和getter方法对其进行操作。
如果一个类的构造方法只有一个,并且使用private修饰,这意味着不可以在类的外部创建对象。很多工具类,比如Arrays、Collections、Maths都是如此。
Java的反射技术可以突破封装性的限制,即使private成员也可以被访问。
6. 写出java.lang.Object类的六个常用方法及其作用
Object类是Java中所有类的顶级类,如果一个类定义是没有指明父类,默认就是继承Object类。能够放在Object类中的方法,肯定都是“了不起”的方法
-
public boolean equals(java.lang.Object) 比较对象内容
-
public native int hashCode() 计算哈希码
-
public java.lang.String toString() 将对象转换为字符串
-
public final void wait() throws InterruptedException 多线程中等待功能
-
public final native void notify() 多线程中唤醒功能
-
public final native void notifyAll() 多线程中唤醒所有等待线程的功能
-
public final native java.lang.Class getClass() 获取类结构信息,一般和反射有关
-
protected void finalize() throws Throwable 垃圾回收前执行的方法
-
protected native Object clone() throws CloneNotSupportedException 克隆,就是快速复制
7. static关键字的作用
总:
- static是Java中的一个关键字,单词本身是静态的含义
- 使用static修饰的成员成为静态成员,是属于某个类的;而不使用static修饰的成员成为实例成员,是属于类的每个对象的。
- 一个类的成员包括变量、方法、构造方法、代码块和内部类,static可以修饰除了构造方法以外的所有成员。
分:
-
static修饰变量
static属性属于这个类所有,即由该类创建的所有对象共享同一个static属性。
static变量和非static变量都是成员变量,这是共同点,区别主要有:1).在内存中份数不同
不管有多少个对象,static变量只有1份。对于每个对象,实例变量都会有单独的一份
非static变量好比是水杯,1个学生1个;static变量好比是饮水机,大家共享2.在内存中存放的位置不同
静态变量存在方法区中,实例变量存在堆内存中3.访问的方式不同
实例变量:对象名.变量名
静态变量:对象名.变量名,不推荐如此使用;类名.变量名,推荐使用4.在内存中分配空间的时间不同
实例变量:创建对象的时候才分配了空间。
静态变量:第一次加载类的时候; -
static修饰方法
可以通过对象名.方法名和类名.方法名两种方式来访问 -
static修饰代码块
当类被第一次加载时(可能是调用static属性和方法,或者创建其对象)执行静态代码块,且只被执行一次,主要作用是实现static属性的初始化 -
static修饰内部类
属于整个外部类,而不是属于外部类的每个对象。不能访问外部类的非静态成员(变量或者方法),可以访问外部类的静态成员 -
静态导入
import static java.lang.Math.*; 非导入类和接口,而是导入类和接口的静态成员
总:
- Java中Math、Arrays、Collections等工具类中大量的static属性和方法,通过类名直接调用即可。Integer类中有一个静态内部类IntegerCache
- Class.forName( "com.mysql.jdbc.Driver");的作用其实就是第一次加载类的时候调用该类的静态代码块,实现驱动注册。
- 接口中是否可以出现static方法;在JDK1.7之前是不可以的。在JDK1.8中可以出现static方法,但不能是抽象方法。
(敢提到这些类,应该对这些类有了解吧。面试官会问:JDK8中接口有什么新特征?(允许我们在接口中定义static方法和default方法,必须要写方法的实现,实现该接口的类可以不重写静态和default方法 )IntegerCache的作用是什么?Math类构造方法private是怎么回事)。
可选:
-
局部变量不可以使用static修饰
-
static不能和abstract共存;
因为static修饰的方法是静态方法,其可以直接被类所调用。而abstract修饰的方法为抽象方法,即无方法体的方法,不能够被直接调用,需要在子类或实现类中去编写完整的方法处理逻辑后才能使用。
异常(异常的超类是Throwable,Throwable又是Object的子类)
异常实际上是程序中意外导致中断了正常的指令流的一种事件。
Throwable的子类有Error(错误)和Exception(异常)。
Error:是合理的应用程序不应该试图捕获。
Exception:又分运行时异常(RuntimeException)和编译时异常(Exception)。所有异常的超类,程序产生并抛出异常,需要程序员去处理的。
- runtimeException:运行期异常,编译期不报错,运行期间可能会产生异常,不处理遇到异常时程序会直接中断。
- Exception:编译期异常,编译期直接报错,必须处理,否则程序无法运行。
常见的运行期异常
- NullPointException 空指针异常
- ClassCastException 类型转换异常
- IndexOutOfBoundsException 排序索超出范围
- ArrayIndexOutBoundsException 数组下标越界异常
- ClassNotFoundException 找不到类异常
- ArtimeticException 算术异常
- FileNotFoundException 文件找不到
- EOFException 意外地到达文件尾或流尾的信号
- illegalArgumentException 不合法的参数
反射的作用;反射的应用优势;实际应用中的用法
什么是反射?
指程序运行时可以访问、检测和修改它本身状态或行为的一种能力。
反射是一种能力,所以给的定义就是说明了它能干嘛。
反射加载类,并解剖出类的各个组成部分,如:成员变量、方法、构造方法。
反射使Java变得更加强大,它可以做很多,很多优秀开源框架都是通过反射完成的,反射是框架的灵魂,它可有效地降低类之间耦合。
简单来说:使用new创建对象,是在编码编译时已经知道类的名称了,不需要使用反射。如果编码编译时不知道要创建对象的类名称,需要根据运行时信息来获取类的信息(比如将类名称放入到properties文件或者xml文件中),该怎么办呢?new是不行了,反射就是来解决该问题的。所以反射使Java功能变得强大很多。
反射的功能?
在运行时判断任意一个对象所属的类
在运行时构造任意一个类的对象
在运行时判断任意一个类所具有的成员变量和方法;
在运行时调用任意一个对象的方法
生成动态代理。
反射优点?
反射提高了程序的灵活性和扩展性。
降低耦合性,提高自适应能力。
突破权限修饰符的限制,可以突破泛型的限制
它允许程序创建和控制任何类的对象,无需提前硬编码目标类。
反射的缺点?
- 性能问题:使用反射基本上是一种解释操作,用于字段和方法接入时要远慢于直接代码。因此反射机制主要应用在对灵活性和拓展性要求很高的系统框架上,普通程序不建议使用。
- 使用反射会模糊程序内部逻辑;程序员希望在源代码中看到程序的逻辑,反射却绕过了源代码的技术,因而会带来维护的问题,反射代码比相应的直接代码更复杂。代码可读性差
为什么静态方法不能被重写?
不可以
静态方法可以被继承,但是,不能被覆盖,即重写。
重写的目的在于根据创造对象的所属类型不同而表现出多态。因为静态方法无需创建对象即可使用。没有对象,重写所需要的“对象所属类型” 这一要素不存在,因此无法被重写。
代码中,如果子类写了一个跟父类同样的静态方法,那么这个静态方法是属于子类的,如果在子类方法上面加上@Override,编译就会报错。
通过一个指向子类对象的父类引用变量来调用父子同名的静态方法时,只会调用父类的静态方法。
在进行方法调用时,系统唯一的任务是确定被调用方法的版本。对于private、static、final方法或者构造器,这部分方法在程序真正运行之前就有一个可以确定的调用版本,并且该版本在运行期间是不可变的,编译器一开始就能确定要调用的版本,这叫做静态绑定,这些方法在类加载的时候就会把符号引用转化为该方法的直接引用。与之对应,在程序运行期间确定方法调用版本的调用方式叫做动态绑定,此时,虚拟机会为每个类创建一个方法表,列出所有方法签名和实际调用的方法,这样一来虚拟机在调用方法时,只用查找该表就行了,只有在调用时采用动态绑定的方法才能体现出多态特性。
有了接口,为什么还需要抽象方法?
接口实现是有implements,抽象类对象的关键字是abstract
接口和抽象方法是有区别的?主要体现在两个方法上,语法层面上和设计理念上
语法层面上
- 成员属性:接口中的属性只能定义全局静态常量,而抽象类中可以定义常量和变量。
- 成员方法:JDK8之前接口中的方法都是全局抽象方法,不能有方法体。而抽象类中可以有任意数量的抽象方法,抽象方法必须在抽象类中,抽象类中可以没有抽象方法。
- 构造方法:抽象类中可以有构造方法,但是不能用来实例化,而在子类实例化时执行,完成属于抽象类的初始化操作。接口中不能定义构造方法。
- 一个类,只能有一个直接的父类(可以是抽象类),但是可以同时有多个接口。
设置理念上
- 抽象类体现了一种继承关系,目的是复用代码。父类和子类之间是 is - a 的关系,即父类和子类在概念本质上是相同的。而接口仅仅是定义了某种约定和能力,具体怎么做,还是需要继承者来实现。接口和实现类之间可以认为是一种has-a的关系。
- 抽象类是对事物的抽象,即对类的抽象,而接口是对行为的抽象。
举个网上流传最广泛的例子:门和警报的例子
门都有open()和close()两个动作,此时我们可以通过抽象类和接口来定义这两个抽象概念。
abstract class Door {
public abstract void open();
public abstract void close();
}
或者:
interface Door {
public abstract void open();
public abstract void close();
}
但是现在如果我们需要门具有报警alarm()的功能,那么该怎么实现?
1)将这三个功能都放到抽象类里面,但是这样一来所有继承于这个抽象类的子类都具备了这个报警的功能,但是有的门并不具备报警功能;
2) 将这三个功能全部都放到接口里面,需要用到报警功能的类就需要实现接口的open()和close()方法,也许这个类根本就不具备open()和close()这两个功能,比如:火灾报警器。
从这里可以看出。Door的open()、close()和alarm()方法属于两个不同范畴的行为。open()和close()是门固有的两个行为,而alarm()属于延伸的附加行为。最好的方法是单独将报警设计为一个接口,包含alram()行为。Door设置成抽象类,包含open()和close()两个方法。
那么什么时候使用抽象类和接口?
- 如果你拥有一些方法并且想让它们中的一些有默认实现,那么就使用抽象类。
- 如果你想实现多重继承,那么就使用接口,Java不支持多继承,但是可以多实现。
- 如果基本功能在不断改变,那么就需要使用抽象类。如果不断改变基本功能并且使用接口,那么就需要改变所有实现了该接口的类。