希望可以通过几周的时间好好巩固一下 Java 基础知识,之后顺利找到一份实习吧
注:所有的题目都是转自 Java之音,他们的公众号也做的不错,可以关注下
请说出作用域public, private, protected, 以及不写时的区别
- public:表成员变量和函数可以被类、子类、同一个包中的类以及任意其他类访问
- private:修饰的成员变量和函数只能在类本身和内部类中被访问,其他地方都不能访问
- protected:成员变量和函数能被类本身、子类及同一个包中的类访问
面向对象的特征有哪些?
继承、封装、多态
- 继承:保留父级的属性,开拓新的东西,字类继承父类的所有状态,同时添加自身的状态
- 封装:类的私有化,比如getter、setter方法
- 多态:有继承关系、子类重写父类方法、父类引用指向子类对象
char型变量中能不能存贮一个中文汉字?为什么?
char 型变量是用来存储 unicode 编码的字符的,unicode 编码字符集中包含了汉字,所以,char 型变量中可以存储汉字。不过,如果某个特殊的汉字没有被包含在unicode 编码字符集中,那么,这个 char 型变量中就不能存储这个特殊汉字。补充说明: unicode 编码占用两个字节,所以,char 类型的变量也是占用两个字节。
&和&&的区别
- & 和 && 都可以作为逻辑与的运算符,都表示 and,当运算符两边的表达式的结果都为true时,整个运算结果才为true,否则,只要有一方为false,则结果为false。
- && 还具有短路的功能,即如果第一个表达式为false,则不再计算第二个表达式,直接结果就是 false,而如果是 &,则还会继续执行第二个表达式
- & 还可以作为位运算符,比如二进制数相与,相同位的两个数字都为1,则为1;若有一个不为1,则为0。
为什么String是不可变的
public final class String
implements java.io.Serializable, Comparable, CharSequence {
/** The value is used for character storage. */
private final char value[];
/** Cache the hash code for the string */
private int hash; // Default to 0
value是String封装的数组,value中的所有字符都是属于String这个对象的。由于value是private的,并且没有提供setValue等公共方法来修改这些值,所以在String类的外部无法修改String。也就是说一旦初始化就不能修改。
此外,value变量是final的, 也就是说在String类内部,一旦这个值初始化了,value引用类型变量所引用的地址不会改变,即一直引用同一个对象。所以可以说String对象是不可变对象。
字符串常量池
字面量形式:String str = "Hello"
当代码中出现字面量形式创建字符串对象时,JVM首先会对这个字面量进行检查,如果字符串常量池中存在相同内容的字符串对象的引用,则将这个引用返回,否则新的字符串对象被创建,然后将这个引用放入字符串常量池,并返回该引用
创建对象形式:String str = new String("hello")
当代码中出现了new来构造字符串对象的时候,不管字符串常量池中有没有相同内容的对象的引用,首先它都会去创建这个字符串对象,这里字符串对象指的是String对象,String对象存放在堆当中。然后它会去字符串常量池寻找Hello这个字符串,其处理的结果同字面量形式。最终str引用指向String对象的引用
String str = new String(“abc”) 创建多少个对象?
- 先在常量池种查找是否有“abc”对象,有则返回对应的引用实例,没有则创建对应的实例对象
- 在堆中new一个String("abc")对象
- 将对象地址赋值给str,创建一个引用
综上,常量池中没有“abc”字面量,则创建两个对象,否则创建一个对象,以及一个引用
switch语句能否作用在byte上,能否作用在long上,能否作用在String上?
由于 byte,short 都可以隐式的转换为 int,因此可以。long 并不能隐式的转为 int,因此不能作用在 switch 语句中。string 类型在 jdk1.7 之后可以作用在 switch 中了。
short s=1;s=s+1;与 short s=1;s+=1;
- short s=1;s=s+1 这段代码会报错。原因在于,前面的 s 是 short 类型的,而后面的 1 因为是 int 类型的,s+1 的返回值就是 int 型的,由于隐式类型转换的问题,int 赋给 short 就会出现精度下降的错误提示
- short s=1;s+=1 这段代码正确。因为,+= 是操作符, s+=1 等价于 s=(short)(s+1)
这里提一下 隐式类型转换,由编译器自动执行,按从小的数据类型往大的数据类型的规则自动转换,即 byte->short->int->long,此时如果反过来,会丢失精度。这个时候,必须要用到我们经常使用的 显式类型转换 了,比如题目中的第一种情况,我们可以使用 s=(short)s+1 进行强制类型转换
使用final关键字修饰一个变量时,是引用不能变,还是引用的对象不能变?
使用 final 关键字修饰一个变量时,是指引用变量不能变,引用变量所指向的对象中的内容还是可以改变的
final StringBuilder a = new StringBuilder("");
a = new StringBuilder("nihao"); //报错
a.append("hello world"); //正确
当 final 修饰一个基本数据类型的变量时,这个变量相当于是常量了,不能被改变,如果 final 修饰一个引用变量,该变量存了一个内存地址,那么该引用变量存的地址是无法改变的,但是可以改变该引用变量指向的对象的值。
静态变量和实例变量的区别?
- 语法上:静态变量需要使用 static 修饰,而实例变量不需要
- 程序运行上: 实例变量属于对象的属性,只有创建了对象的实例之后,其中的实例变量才可以被分配空间。静态变量即类别量,只要程序加载了类的字节码,不用创建任何实例对象,静态变量就会被分配空间。综上,实例变量必须创建对象后通过这个对象来使用,静态变量可以直接使用类名来引用
public class Test {
public static int a1 = 0;
public int a2 = 0;
public Test() {
a1++;
a2++;
System.out.println(a1 + " " + a2);
}
public static void main(String[] args) {
Test test = new Test(); //1 1
Test test2 = new Test(); //2 1
Test test3 = new Test(); //3 1
}
}
由输出结果知道,类变量是所有对象共有,其中一个对象将它值改变,其他对象得到的就是改变后的结果;而实例变量则属对象私有,某一个对象将其值改变,不影响其他对象
接口是否可继承接口?抽象类是否可实现(implements)接口?抽象类是否可继承具体类(concreteclass)?抽象类中是否可以有静态的main方法?
接口可以继承接口。抽象类可以实现(implements)接口,抽象类可以继承具体类。抽象类中可以有静态的main方法。
备注:只要明白了接口和抽象类的本质和作用,这些问题都很好回答,你想想,如果你是java语言的设计者,你是否会提供这样的支持,如果不提供的话,有什么理由吗?如果你没有道理不提供,那答案就是肯定的了。
只要记住抽象类与普通类的唯一区别就是不能创建实例对象和允许有abstract方法。
abstractclass和interface语法上有什么区别?
- 抽象类可以有构造方法,接口中不能有构造方法。
- 抽象类中可以有普通成员变量,接口中没有普通成员变量
- 抽象类中可以包含非抽象的普通方法,接口中的所有方法必须都是抽象的,不能有非抽象的普通方法。
- 抽象类中的抽象方法的访问类型可以是public,protected,但接口中的抽象方法只能是public类型的,并且默认即为public abstract类型
- 抽象类中可以包含静态方法,接口中不能包含静态方法
- 抽象类和接口中都可以包含静态成员变量,抽象类中的静态成员变量的访问类型可以任意,但接口中定义的变量只能是publicstatic final类型,并且默认即为publicstatic final类型。
- 一个类可以实现多个接口,但只能继承一个抽象类。
Java中的String,StringBuilder,StringBuffer三者的区别
- 速度上:StringBuilder > StringBuffer > String。
原因是:String为字符串常量,而StringBuilder和StringBuffer均为字符串变量,即String对象一旦创建之后该对象是不可更改的,但后两者的对象是变量,是可以更改的 - 线程安全上:StringBuilder是线程不安全的,而StringBuffer是线程安全的
原因是:如果一个StringBuffer对象在字符串缓冲区被多个线程使用时,StringBuffer中很多方法可以带有synchronized关键字,所以可以保证线程是安全的,但StringBuilder的方法则没有该关键字,所以不能保证线程安全,有可能会出现一些错误的操作。所以如果要进行的操作是多线程的,那么就要使用StringBuffer,但是在单线程的情况下,还是建议使用速度比较快的StringBuilder。
理解final
- 修饰类:当用final修饰一个类时,表明这个类不能被继承。也就是说,如果一个类你永远不会让他被继承,就可以用final进行修饰
- 修饰方法:当用final修饰一个方法时,该方法不能被继承,也不能被修改
- 修饰变量:对于一个final变量,如果是基本数据类型的变量,则其数值一旦在初始化之后便不能更改;如果是引用类型的变量,则在对其初始化之后便不能再让其指向另一个对象
static
- 被static关键字修饰的方法或者变量不需要依赖于对象来进行方法,只要类被加载了,就可以通过类名去访问
- 静态方法不依赖于任何对象就可以进行访问,静态方法中不能访问类的非静态成员变量和非静态成员方法,因为非静态成员方法/变量都是必须依赖具体的对象才能够被调用。
error和exception的区别
- error:Error是无法处理的异常,比如OutOfMemoryError,一般发生这种异常,JVM会选择终止程序。因此我们编写程序时不需要关心这类异常。
- exception:Exception表示一种设计或实现问题。也就是说,它表示如果程序运行正常,从不会发生的情况,比如NullPointerException、IndexOutOfBoundsException,这些异常是我们可以处理的异常
字节流与字符流的区别
实际上字节流在操作时本身不会用到缓冲区(内存),是文件本身直接操作的;而字符流在操作时使用了缓冲区,通过缓冲区再操作文件
当使用字节流和字符流对同一文件进行写入操作时,如果操作完成后都没有关闭,则使用了字节流的文件依旧可以将写入的内容输出,可以证明字节流是直接操作文件本身的;而如果使用字符流,则发现不能输出任何内容。这是因为字符流操作时使用了缓冲区,而在关闭字符流时会强制性地将缓冲区中的内容进行输出,但是如果程序没有关闭,则缓冲区中的内容是无法输出的
Java 中,throw 和 throws 有什么区别
- throws出现在方法函数头;而throw出现在函数体。
- throws表示出现异常的一种可能性,并不一定会发生这些异常;throw则是抛出了异常,执行throw则一定抛出了某种异常对象。
- 两者都是消极处理异常的方式(这里的消极并不是说这种方式不好),只是抛出或者可能抛出异常,但是不会由函数去处理异常,真正的处理异常由函数的上层调用处理。
equals和==
- 对于==,如果作用于基本数据类型的变量,则直接比较其存储的 “值”是否相等;如果作用于引用类型的变量,则比较的是所指向的对象的地址
- 对于equals方法,该方法不能作用于基本数据类型的变量,如果没有对equals方法进行重写,则比较的是引用类型的变量所指向的对象的地址;诸如String、Date等类对equals方法进行了重写的话,比较的是所指向的对象的内容。
堆和栈有什么区别
- 栈:在函数中定义的一些基本类型的变量和对象的引用变量哦都是在函数的栈内存中分配的,当在一段代码块定义一个变量时,Java 就在栈中为这个变量分配空间,当超过变量的作用域后,Java 会自动释放掉为该变量分配的内存空间。
- 堆:堆内存用来存放由 new 创建的对象和数组,在堆中分配的内存,由 Java 虚拟机的自动垃圾回收器来管理。
a.hashCode() 有什么用?与 a.equals(b) 有什么关系?
hashCode() 方法对应对象整形的hash值,它常用于基于hash的集合类,如Hashtable,HashMap,LinkedHashMap等,如果两个使用equal() 方法来判断相等的对象,必须具有相同的hash code
JVM加载clss文件的原理机制
JVM类的装载是由ClassLoader和它的子类来实现的,Java ClassLoader是一个重要的Java运行时系统组件,负责在运行时查找和装入类文件的类
GC是什么?为什么要有GC?
GC是垃圾回收的意思,内存处理是编程人员容易出现问题的地方,忘记或者错误的内存回收会导致程序或者系统的不稳定甚至崩溃,Java提供的GC功能可以自动检测对象是否超过作用域从而达到自动回收内存的目的。
垃圾回收器的基本原理是什么?垃圾回收器可以马上回收内存吗?有什么办法可以通知虚拟机进行垃圾回收?
对于GC来说,当程序员创建这个对象的时候,GC就开始监控这个对象的地址、大小以及使用情况。GC通常采用有向图的方式记录和管理堆中的所有对象。通过这个方式确定一些对象是不可达时,GC就有责任回收这些内存空间
程序员可以手动执行System.gc(),通知GC运行,但是Java语言规范并不保证GC一定会执行
java中存在内存泄露吗?
内存泄露:指一个不再被程序员使用的对象或变量一直被占据在内存中,Java中有垃圾回收机制,可以保证当对象不再被引用的时候,对象将自动被垃圾回收器从内存中清除
内存泄漏的情况:长生命周期的对象持有短生命周期对象的引用即会发生内存泄露,尽管短生命周期对象已经不再需要,但是因为长生命周期对象持有它的引用而导致不能被回收,这就是java中内存泄露的发生场景,通俗地说,就是程序员可能创建了一个对象,以后一直不再使用这个对象,这个对象却一直被引用,即这个对象无用但是却无法被垃圾回收器回收的,这就是java中可能出现内存泄露的情况
什么是 对象/关系 映射集成模块
Object Relation Mapping,即对象关系映射,是面向对象编程中的对象和关系数据库的关系的一个映射
JRE、JDK、JVM 及 JIT 之间有什么不同
- JVM:Java虚拟机,认识xxx.class这种类型的文件,能够将class中的字节码指令进行识别并调用操作系统的向上API完成动作
- JRE:Java运行时环境,包括JVM标准实现和Java的基本类库
- JDK:Java开发工具包,整个Java开发的核心,集合了JRE和一些好用的工具
- JIT:即时编译器是种特殊的编译器,它通过有效的把字节码变成机器码来提高JVM的效率
什么是IOC和DI
- IOC:控制反转,传统的开发中,需要调用对象时,都是由调用者自己来创建被调用的实例的,即对象是由调用者主动new出来的。但在Spring中,创建对象的工作不是由调用者来完成的,而是交给IOC容器来创建,在推送给调用者,整个流程完成反转,所以是控制反转
去购物,需要你自己拿着袋子去超时去选择商品,而如果是IOC的话,你需要把袋子放在家门口,然后超时会自动在袋子里帮你装满你需要的商品,你要做的只是打开袋子就能使用
- DI:在Spring.xml中,通过ref属性将其他bean赋值给当前bean对象的过程,叫做依赖注入,是Spring非常重要的机制,DI是将不同对象进行关联的一种方式,是IOC的具体实现方式,通常DI和IOC是紧密结合在一起的,所以一般说的IOC包括DI。
什么是AOP
面向切面编程。动态的将代码切入到类的指定方法,指定位置,这种编程思想就是面向切面编程。将不同方法的同一位置抽象成一个切面对象,对该切面对象进行编程就是AOP。
优点
- 降低模块之间的耦合度
- 使系统变得容易扩展
- 更好的代码复用
- 非业务代码更加集中,不分散,便于统一管理