1.Java三大特性:封装、继承、多态。
封装:把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或对象操作,对不可信的类进行信息隐藏。
继承:可以使用现有类的所有功能,并且可在无需编写原来的类的情况下对这些功能进行扩展。
多态:在父类中定义的属性和方法被子类继承之后,可以具有不同的数据类型或表现出不同的行为,使得同一属性或方法在父类及其个子类中有不同的含义。
注:多态分为编译时多态(如:重载)和运行时多态
编译时多态是编译时就已经确定,运行时调用的是确定的方法。
运行时多态指在编译是不确定究竟调用哪个具体的方法,一直延迟到运行时才确定。我们通常所说的多态指的是运行时多态。
多态的三个必要条件:继承、重写、向上转型。
2.jdk、jre和jvm的关系:
jdk包含jre,jre包含jvm。
jdk:功能齐全的Java sdk,包含jre,拥有编译器(javac)和工具(javadoc),可以创建和编译程序。
jre:运行已编译Java程序所需要的内容集合,包括JVM、java类库、Java命令和其他基础构件,但不能用于创建新程序。
jvm:Java虚拟环境。
3.static关键字
“static"关键字表明一个成员变量或成员方法可以在没有所属类的实例变量的情况下被访问。
static方法不能被覆盖,方法覆盖是基于运行时动态绑定,而static方法是编译时静态绑定的。
static方法跟类的任何实例都不相关。
4.java静态变量、代码块和静态方法的 执行顺序:
父类静态代码块--子类静态代码块--父类代码块--父类构造器--子类代码块--子类构造器
5.重载和重写的区别:
方法的重写和重载都是实现多态的方式,区别在于重写实现的是运行时多态,重载实现的是编译时多态。
重写:发生在子类与父类之间,方法名、返回值类型和形参都不能改变。
重载:在一个类里面,方法名相同,参数列表不同。返回类型可以相同也可以不同。
6.Java创建对象的几种方法:
new关键字创建对象;
通过反射机制创建对象;
采用clone机制创建对象;
通过序列化机制创建对象;
前两种方法需要显示的调用构造方法,clone机制需要注意浅拷贝和深拷贝的区别,序列化可以通过实现Externalizable或Serializabel来实现。
7.接口和抽象类的区别:
接口:接口中的方法必须都是public abstract方法;成员变量只能是public static final类型;内部不能含有静态代码块以及静态方法;一个类可以实现多个接口。
抽象类:抽象类中可以提供成员方法实现细节;成员变量可以是各种类型;内部可以包含静态代码块和静态方法;一个类只能继承一个抽象类。
注:抽象类不能使用final关键字修饰。
8.什么是不可变对象?
不可变对象即对象一旦被创建,其状态就不能再改变,任何修改都会创建一个新的对象,如String、Integer以及其他的包装类。不可变对象的最大好处是线程安全。
9.==和equals的区别:
==:比较基本数据类型时,比较的是值是否相同;比较引用数据类型时,比较的是地址。
equals:只能用在引用数据类型中。如果所比较变量的所在类没有重写Object类中的equals方法,则比较地址;否则,比较变量的属性值(如String类中的对象比较)。
10. 什么是HashCode? hashCode()和equals()方法的关系?
hashCode()的作用是获取哈希码,也称为散列码;其实际返回的是一个int整数。其作用是确定对象在哈希表中的索引位置。
为什么要有hashCode:当你把对象加入Hashset中时,Hashset先计算hashcode判断对象加入的位置,并于其他已经加入对象的 hashcode值作比较,如果没有相同的,则对象没有出现过;否则,需要通过equals()方法来检查hashcode相等的对象是否真的相同,相同则Hashset不会让其操作成功。不相同,则会重新将其散列到其他位置。这样以减少equals方法的调用次数,提高执行速度。
如上图所示,对于hashcode方法,会返回一哈希值,哈希值对数组的长度取余后会确定一个存储的下标位置,如图中用数组括起来的第一列。不同的哈希值取余后的结果可能是相同的,用equals方法判断是否为相同的对象,不同则在链表中插入。
11.String、StringBuffer和StringBuilder的区别?
String不可变:String类中使用字符数组保存字符串,因为有“final”修饰符(源码如下),所以String对象是不可变的。对于已经存在的String对象的修改都是重新创建一个新的对象,然后把新的值保存进去。
private final char value[];
StringBuffer和StringBuilder可变:StringBuffer和StringBuilder都是继承AbstractStringBuilder类,在AbstractStringBuilder类中也是使用字符数组保存字符串(源码如下),这两种对象都是可变的。
char[] value;
注:String中的对象是不可变的,也就可以理解为常量,显然线程安全;
StringBuffer是非线程安全的;
StringBuilder对方法或调用方法加了同步锁,所以是线程安全的。
12.String为什么要设计成不可变的?
①便于实现字符串池(String pool)----在Java中,由于会大量使用String常量,如果每次声明都创建对象,那将造成极大的空间资源浪费。对此,Java提出String pool的概念,在堆中开辟一块存储空间String pool,当初始化一个String变量时,如果该字符串已经存在了,就不会去创建一个新的字符串变量,而是会返回已经存在了的字符串的引用。
②使多线程安全----并发场景下,多线程对同一个资源读取是安全的,多线程对同一个资源进行写操作时是不安全的,而不可变对象不能被写,所以保证了多线程的安全。
③避免安全问题----在网络链接和数据库链接中字符串常常作为参数,如:URL、文件路径path、反射机制所使用的String参数,其不可变性可以保证连接的安全性。
④加快字符串处理速度----由于String是不可变的,保证了hashcode的唯一性,于是在创建对象时hashcode就可以放心的存入缓存,不需要重新计算。
13.什么是字符串常量池?
Java中的三个常量池:全局字符串常量池、class文件常量池、运行时常量池。
jvm为了提升性能和减少内存开销,避免字符的重新创建,其维护了一块特殊的内存空间,及字符串池。当需要使用字符串时,先去字符串池中查看该字符串是否已经存在,如果存在,则可以直接使用;如果不存在,初始化并将该字符串放入字符串常量池中。
jdk6:常量池的位置在永久代(方法区)中,此时常量池中存储的是对象。
jdk7:常量池的位置在堆中,常量池中存储的是引用。
jdk8:存储在永久代(方法区),被元空间取代了。
14.什么是反射?如何获取反射中的class对象?
反射是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取信息以及动态调用对象的方法称为Java的反射机制。
获取class对象:
①Class.forName("类的全路径名");
②类名.class(注意:仅适合在编译阶段前就知道操作的Class);
③对象名.getClass();
④如果是基本类型的包装类,可以调用包装类的Type属性来获得该包装类的Class对象。
15.java反射API类型:
反射API用来生成JVM中的类、接口或对象信息。
①Class类:反射的核心类,可以获取类的属性、方法等信息;
②Field类:Java.lang.reflect包中的类,表示类的成员变量,可以用来获取和设置类之中的属性值;
③Method类:Java.lang.reflect包中的类,表示类的方法,可以用来获取类中的方法信息或执行方法;
④Constructor类:Java.lang.reflect包中的类,表示类的构造方法。
16.反射机制的步骤:
①通过Class.forName("全路径类名")获取类的Class对象实例;
②Class对象实例通过调用getConstructor()方法创建Constructor对象;
③通过Constructor对象调用newInstance()方法获取反射类对象;
④如果调用方法,可以使用Class对象实例调用Method("方法名",参数类型.class)方法来获取Method对象;
⑤Method对象利用invoke(反射类对象,参数)方法调用方法
17.Java中的泛型是什么?泛型的好处和泛型的原理?
泛型是jdk1.5的一个新特性,泛型就是将类型参数化,其在编译时才确定具体的参数。
使用泛型可以保证:类型安全、消除强制类型转换等问题。
原理:泛型是一种语法糖,其基本原理是类型擦除。Java中的泛型基本上都是在编译器这个层次来实现的。即,泛型只存在于编译阶段而不存在于运行阶段;在编译后的class文件中,是没有泛型这个概念。
类型擦除:使用泛型的时候加上的类型参数,编译器在编译的时候去掉类型参数。
18.序列化
Java序列化是指把Java对象转化为序列化的过程,而Java反序列化是指把字节序列恢复为Java对象的过程。
序列化:序列化是把对象转换成有序的字节流,以便于在网络上传输和保存在本地文件中 。其核心作用是对象状态的保存和重建。
反序列化:客户端从文件中或网络上获取序列化后的对象字节流,根据字节流中所保存的对象状态及描述信息,通过反序列化重建对象。
序列化实现的方式:
①实现Serializable接口
②实现Externalizable接口,该接口继承自Serializabel,且接口中定义了两个抽象方法:writer External()和readExternal()。
注:当使用Externalizable接口进行序列化和反序列化时需要重写writer External()和readExternal()这两个方法。否则所有的变量的值都会变成默认值。
什么时SerialVersionUID?
SerialVersionUID是用来表明类的不同版本间的兼容性。Java序列化机制就是通过在运行时判断类的SerialVersionUID来验证版本一致性的。
19.异常
Java中所有异常都有一个共同的祖先java.lang包中的Throwable类。Throwable类有两个重要子类Exception(异常)和Error(错误)。
Error和Exception的区别:
Exception:程序本身可以处理的异常,可以通过catch来进行捕获,对其进行处理好后程序可以继续运行。Exception又可以分为运行时异常(RuntimeException,又称非受检查异常)和非运行时异常(又称受检查异常)。
Error:属于程序无法处理的错误,无法通过catch捕获。错误一旦发生,通常应用程序会被中止,仅靠应用程序本身无法恢复。
NoClassDefFoundError 和 ClassNotFoundException 区别?
NoClassDefFoundError 是一个 Error 类型的异常,是由 JVM 引起的,不应该尝试捕获这个异常。引起该异常的原因是 JVM 或 ClassLoader 尝试加载某类时在内存中找不到该类的定义,该动作发生在运行期间,即编译时该类存在,但是在运行时却找不到了,可能是编译后被删除了等原因导致。
ClassNotFoundException 是一个受检查异常,需要显式地使用 try-catch 对其进行捕获和处理,或在方法签名中用 throws 关键字进行声明。当使用 Class.forName, ClassLoader.loadClass 或 ClassLoader.findSystemClass 动态加载类到内存的时候,通过传入的类路径参数没有找到该类,就会抛出该异常;另一种抛出该异常的可能原因是某个类已经由一个类加载器加载至内存中,另一个加载器又尝试去加载它。
Java常见异常有哪些?
java.lang.IllegalAccessError:违法访问错误。当一个应用试图访问、修改某个类的域(Field)或者调用其方法,但是又违反域或方法的可见性声明,则抛出该异常。
java.lang.InstantiationError:实例化错误。当一个应用试图通过Java的new操作符构造一个抽象类或者接口时抛出该异常.
java.lang.OutOfMemoryError:内存不足错误。当可用内存不足以让Java虚拟机分配给一个对象时抛出该错误。
java.lang.StackOverflowError:堆栈溢出错误。当一个应用递归调用的层次太深而导致堆栈溢出或者陷入死循环时抛出该错误。
java.lang.ClassCastException:类造型异常。假设有类A和B(A不是B的父类或子类),O是A的实例,那么当强制将O构造为类B的实例时抛出该异常。该异常经常被称为强制类型转换异常。
java.lang.ClassNotFoundException:找不到类异常。当应用试图根据字符串形式的类名构造类,而在遍历CLASSPAH之后找不到对应名称的class文件时,抛出该异常。
java.lang.ArithmeticException:算术条件异常。譬如:整数除零等。
java.lang.ArrayIndexOutOfBoundsException:数组索引越界异常。当对数组的索引值为负数或大于等于数组大小时抛出。
java.lang.IndexOutOfBoundsException:索引越界异常。当访问某个序列的索引值小于0或大于等于序列大小时,抛出该异常。
java.lang.InstantiationException:实例化异常。当试图通过newInstance()方法创建某个类的实例,而该类是一个抽象类或接口时,抛出该异常。
java.lang.NoSuchFieldException:属性不存在异常。当访问某个类的不存在的属性时抛出该异常。
java.lang.NoSuchMethodException:方法不存在异常。当访问某个类的不存在的方法时抛出该异常。
java.lang.NullPointerException:空指针异常。当应用试图在要求使用对象的地方使用了null时,抛出该异常。譬如:调用null对象的实例方法、访问null对象的属性、计算null对象的长度、使用throw语句抛出null等等。
java.lang.NumberFormatException:数字格式异常。当试图将一个String转换为指定的数字类型,而该字符串确不满足数字类型要求的格式时,抛出该异常。
java.lang.StringIndexOutOfBoundsException:字符串索引越界异常。当使用索引值访问某个字符串中的字符,而该索引值小于0或大于等于序列大小时,抛出该异常。
20.IO流分类:
按照流的方向:分为输入流(inputStream)和输出流(outputStream);
按照实现功能:分为字节流可以从(或者向)一个特定的地方读写数据,如FileReader;处理流是对一个已经存在的流的连接和封装,通过所封装的流的功能调用实现数据读取写,如BufferedReader;
按照处理数据的单位:分为字节流和字符流。分别由四个抽象类来表示(每种流包含输入和输出两种,所以一共四个):InputStream,OutputStream,Reader,Writer。
字节流和字符流的区别:
①读写时字节流按字节读写,字符流按字符读写;
②字节流适合所有类型文件的数据传输,因为字节(Byte)是计算机表示信息的最小单位,字符流只能够处理纯文本数据;
③只是读写文件,和文件内容无关时,一般选用字节流。