java基础
1.java概述
1.java语言的特点
- 面向对象:封装,继承,多态
- 平台无关性:虚拟机机制:一次编译,到处运行
- 编译与解释并存
- 网络编程
- 多线程
- 可靠安全
2.JDK 和 JRE和jvm 有什么区别?
jdk>jre>jvm
jdk是java开发工具包,包含jre和编译器javac工具
jre是Java的运行环境,包含jav和类库和java命令
jvm是java虚拟机
3.什么是字节码?
.java源文件编译成十六进制的用于jvm运行的.class文件为java字节码文件
- 使用字节码的好处:一定程度上解决了解释性运行效率低的问题,又保留了可移植的特点
2.基础语法
1.java有哪些数据类型?
数据类型有:int(4字节),short(2字节),long(8字节),byte(1字节),char(2字节),float(4字节),double(8字节),boolean
基本类型 | 位数 | 字节 | 默认值 |
---|---|---|---|
int | 32 | 4 | 0 |
short | 16 | 2 | 0 |
long | 64 | 8 | 0L |
byte | 8 | 1 | 0 |
char | 16 | 2 | ‘u0000‘ |
float | 32 | 4 | 0f |
double | 64 | 8 | 0d |
boolean | 1 | false |
2.修饰符public,protected,default(不写),private的区别?
私有性:public<protected<default(不写)<private
3.关键字
1.final、finally、finalize的区别?
final:
- final变量:不可变
- final方法:不允许子类重写,可以使用
- final类:不能被继承,所有方法不能被重写
finally:异常处理中的try/catch/finally,finally代表最终一定执行,一般用于施放资源
finalize:object下的方法,在垃圾回收GC时,这个对象被回收时使用该方法
2.static关键字
1.为什么要使用static关键字?
为特定域分配单一存储空间,使用static可以不用重复new创建类的对象,直接进行调用
2.Java中是否可以覆盖(override)一个private或者是static的方法
不能,static方法是编译时静态绑定的,方法覆盖是基于运行时动态绑定的
3.是否可以在static环境中访问非static变量?static静态方法能不能引用非静态资源?
不能,static在java虚拟机载入时初始化,而此时非static变量和资源都还没有创建出来
4.static静态方法能不能引用静态资源?
可以,初始化加载静态资源,同级可以引用
3.java静态变量、代码块、和静态方法的执行顺序是什么?
基本上代码块分为三种:Static静态代码块、构造代码块、普通代码块
代码块执行顺序静态代码块——> 构造代码块 ——> 构造函数——> 普通代码块
继承中代码块执行顺序:父类静态块——>子类静态块——>父类代码块——>父类构造器——>子类代码块——>子类构造器
4.面向对象
1.面向对象和面向过程的区别
面向过程:
- 优点:性能更高,消耗资源更少,按顺序执行
- 缺点:没有面向对象易维护、易复用、易扩展。
面向对象:
- 优点:由于java特性,易维护、易复用、易扩展,低耦合
- 缺点:实例化资源开销大,性能更低
2.面向对象三大特性
封装:将具体的事务抽象的封装成类
继承:子类继承父类,具有父类的属性和方法,通过继承可以不改变原来类的情况下进行拓展
多态:子类继承父类后,子类的属性和方法可以具有不同的数据类型和表现出不同的行为
3.java是如何实现多态的?
三个条件:继承,重写,向上转型
- 继承:多态中必须子类继承父类
- 重写:子类重写父类的方法,在调用时是调用子类的方法
- 向上转型:将子类的引用赋给父类对象,在调用时,实际执行子类方法
4.重载(Overload)和重写(Override)的区别是什么?
重载:方法名相同,参数不同,发生在同一个类里面
重写:方法名和参数都相同,发生在父类和子类之间
重载无法根据返回值来进行区分,仅仅返回值不同的重载不被允许
5.构造器是否可以被重载或者重写?
构造器不可以被重写,但是可以重载,子类的构造器要调用父类的构造器
6.抽象类和接口的区别
抽象类中:可以有具体的实现,各种变量,可以有静态方法和静态代码块,只能继承一个抽象类
接口:只能由抽象方法组成,只能有static final 变量,没有静态方法和静态代码块,可以实现多个接口
7.抽象类可以用final修饰吗?
不可以,final禁止继承,抽象类又要被继承,矛盾
8.java创建对象的机制?
- new创建
- 反射,newInstance创建
- clone,克隆机制,从已经存在的对象进行克隆
- 序列化机制,先将对象序列化到磁盘,再反序列化到内存中,即可创建对象
9.什么是不可变对象?有什么好处
创建的对象不可改变,修改会创建一个新的对象,如String,Integer
好处:线程安全
10.能否创建一个包含可变对象的不可变对象
可以,不可变的数组中存入可变的对象引用,final A a[ ]= new a[ ]{ }
11.值传递和引用传递的区别是什么?为什么java只有值传递?
值传递:只传递值
引用传递:传递引用,也就是传递引用的地址
java只有值传递:java的引用传递,传递的是引用地址的值
5.对象相等判断
1.==和equals的区别是什么?
==用于
- 比较基本类型,比较两者的值是否一致
- 比较两个对象,比较两个引用的地址,查看是否指向同一块内存区域
equals用于
- 比较两个对象,看两个对象是否等于另一个对象
2.什么是hashCode()?
用于获取哈希码(也叫散列码),返回一个int值。
值为该对象在哈希列表中的位置,键值对形式存储,通过哈希码找到对象的时空复杂度为1
3.为什么要有hashCode?
HashSet如何检查重复来说明:
- 将对象加入HashSet时,HashSet会先计算hashCode来判断要加入的位置,
- 同时与其他对象的hashCode比较是否有相同的hashCode
- 如果没有则没有重复,如果有相同的,调用equals()方法比较两个对象是否一致
- 一致则不存入,不一致则重写散列到其他位置
4.hashCode()和equals()是什么关系?
- 如果两个对象相等,则hashcode一定也是相同的
- 两个对象相等,对两个对象分别调用equals方法都返回true
- 两个对象有相同的hashcode值,它们也不一定是相等的
5.为什么重写equals方法一定要重写hashCode方法?
判断时,先根据hashCode判断再进行equals判断,如果重写equals方法改变判断,而hashCode不重写,会导致hashCode不一致但是用equals判断为true的情况
6.字符串相关
1.String,StringBuffer, StringBuilder 的区别是什么?
String:不可变,线程安全,改变创建新的对象
StringBuffer:可变,线程安全,有同步锁,改变修改原来的对象
StringBuilder:可变,非线程安全,
2.什么是字符串常量池?
1.7从方法区移到堆中,1.8还是在堆中
jvm为了提升性能和减少内存开销,避免字符的重复创建,其维护了一块特殊的内存空间,即字符串池,当需要使用字符
串时,先去字符串池中查看该字符串是否已经存在,如果存在,则可以直接使用,如果不存在,初始化,并将该字符串放
入字符串常量池中。
3.String为什么要设计成不可变的?
-
便于实现字符串池(String pool)
如果每一个String对象都去创建一个新的String对象,十分浪费空间,所以,在堆中,有字符串池来记录String,初始化一个String变量时,字符串池中有这个字符串,就将返回字符串池中的引用,避免重复创建一样的字符串对象
-
线程安全(读写问题)
字符串不可变,不能写,不会出现竞争问题
-
避免安全问题(外部攻击)
网络和数据库连接中多为字符串形式,不可变可以保证安全性
-
加快字符串处理速度
保证了hashCode的唯一性,在键值对中,可以直接缓存,不用重新计算,处理速度块
4.字符型常量和字符串常量的区别?
字符型常量:
字符char的常量,单引号引起的一个字符,相当于一个整型值( ASCII 值),可以参加表达式运算,只占2个字节
字符串常量:
字符串String的常量,双引号引起的若干个字符,代表一个地址值(该字符串在内存中存放位置,相当于对象)
5.String str="aaa"与 String str=new String("aaa")一样吗?new String(“aaa”);创建了几个字符串对象?
- 使用String a = “aaa” ;,程序运行时会在常量池中查找”aaa”字符串,若没有,会将”aaa”字符串放进常量池,再将其地址赋给a;若有,将找到的”aaa”字符串的地址赋给a。
- 使用String b = new String("aaa");,程序会在堆内存中开辟一片新空间存放新对象,同时会将”aaa”字符串放入常量池,相当于创建了两个对象,无论常量池中有没有”aaa”字符串,程序都会在堆内存中开辟一片新空间存放新对象。
new String(“aaa”)创建了两个字符串对象,一个在堆内存中,一个在字符串常量中
6.String是基本类型吗?
不是,基本类型:short,int,long,char,boolean,float,double和byte
7.String的特性
不变:改变实质为创建新的对象
常量池优化:String对象创建后,会在字符串常量池缓存,下次创建相同的对象,返回池中的引用
final:String用final定义,String类无法继承,系统安全
8.在使用HashMap时,使用String作为key的好处?
String不可变,HashCode唯一,做key时不用重新计算,相对于其他对象更块
7.包装类型
1.包装类型是什么?基本类型和包装类型的区别是什么?
java为每一个基本类型设置了包装类型:
原始类型: boolean,char,byte,short,int,long,float,double
包装类型:Boolean,Character,Byte,Short,Integer,Long,Float,Double
区别:
基本类型不可以为null,而包装类型可以
基本类型不可以适配泛型,而包装类型可以
基本类型比包装类型更加高效
2.解释一下自动装箱和自动拆箱?
自动装箱:自动将基本类型转化为包装类型
声明的包装类型可以用基本类型的方式实例化,不用手动转化类型
Integer num = 9;
自动拆箱:自动将包装类型转化为基本类型
包装类型可以直接以基本类型的方式进行使用,不需要手动转化
num++;
3.int 和 Integer 有什么区别?
Integer是int的包装类,int是基本类型
Integer需要实例化才可以使用,int不需要
Integer实质是对象引用,指向new出的Integer对象,int直接存储数值
Integer默认值为null,int默认值为0
4.两个new生成的Integer变量的对比
实质上为两个对象的引用对比,使用==,结果为false
5.Integer变量和int变量的对比
使用==,由于自动拆箱机制,会转变为基本类型进行对比,结果为true
6.非new生成的Integer变量和new Integer()生成变量的对比
非new生成的Integer变量和new Integer()生成的变量比较时,结果为false。
(因为非new生成的Integer变量指向的是java常量池中的对象,而new Integer()生成的变量指向堆中新建的对象,两者在内存中的地址不同)
Integer b = new Integer(10000);//声明对象--指向堆中对象
Integer c=10000;//自动装箱--指向java常量池
System.out.println(b == c); // false
7.两个非new生成的Integer对象的对比
对于两个非new生成的Integer对象,进行比较时,如果两个变量的值在区间-128到127之间,则比较结果为true,如果两个变量的值不在此区间,则比较结果为false
8.反射
1.什么是反射?
在运行状态下,动态的加载类的属性和方法的机制叫做反射机制
2.反射机制的优缺点?
优点:灵活,运行时获取
缺点:性能较低,需要解析字节码
3.如何获取反射中的Class对象
- Class.forName("类的路径"),通过类的全路径名来获取Class
- 类名.Class
- 对象名.getClass()
- 包装类型.Type
4.java反射的API
- Class--类,可以用class获取属性方法等
- Field--属性
- Method--方法
- Constructor--构造方法
5.反射使用步骤
1.Class c=User.Class;或者Class c=Class.forName("相对路径");等获得Class
2.User u=(User)c.newInstance();无参创建对象或者c.getConstructor(String.class).newInstance("构造参数")创建对象
3.Method m=c.getMethod();或者Field f=c.getField();等获取属性或者方法
4.通过m.invoke(c,"方法参数")或者f.set(c,"值")。如果是私有,需要f.setAccessible(true);关闭程序安全,再操作私有属性
6.反射机制的应用有哪些?
-
jdbc
通过反射加载jdbc的包
-
spring框架
xml配置bean:
- 将配置文件xml或者propreties加载到内存
- 通过内置的工具类进行解析,获取到配置信息
- 通过反射加载类
- 注入到目标属性中
7.反射机制的原理是什么?
9.泛型
1.什么是泛型?
类型参数化,在编译时才确定具体的参数类型,可以用在类,接口,方法中
2.使用泛型有什么好处?
jdk1.4. 没有泛型时,使用Object时
- 每次都要进行强制类型转换
- 编译时无法知道类型,只有运行时才知道,易出错,不安全
例子:
//存入两个不同类型的数据到List中
List list = new ArrayList();
list.add("www.cnblogs.com");
list.add(23);
//取出时进行强制类型转换
String name = (String)list.get(0);
String number = (String)list.get(1); //ClassCastException
使用泛型类型的好处有:
- 类型安全:编译可检测出类型问题,越早出错成本越小
- 消除强制类型转换:直接得到目标类型
- 潜在的性能收益:所有类型工作都在编译器中完成,无需jvm参与
3.Java泛型的原理是什么 ? 什么是类型擦除 ?
泛型原理:
泛型语法是一种语法糖,它的原理就是类型擦除,泛型只存在于编译阶段,不存在执行阶段,编译之后的class文件没有泛型。
(语法糖指计算机语言中添加的某种语法,这种语法对语言的功能并没有影响,但是更方便程序员使用。)
类型擦除:使用泛型的时候加上类型参数,编译器在编译的时候去掉类型参数。
如:
//<T> 泛型参数
public class Caculate<T> {
private T num;
}
大部分情况下,编译器会将T编译为Object类型,替换掉泛型
//擦除结果
public class Caculate{
public Caculate(){}
private Object num;
}
使用到了extends和super语法的有界类型,将不是直接以Object类型进行擦除,使用指定的类型进行擦除
4.什么是泛型中的限定通配符和非限定通配符 ?
限定通配符:extends和super:用于限定?
? extends T: 指定为改类子类
? super T:指定为改类父类
非限定通配符:?:表示类型为任何类型,未知的类型
5.可以把List<String>
传递给一个接受List<Object>
参数的方法吗?
不可以,Object范围比String大,会导致编译错误
6.Array中可以用泛型吗?
不可以。因为 List 可以提供编译期的类型安全保证,而 Array 却不能。
7.判断ArrayList<String>
与ArrayList<Integer>
是否相等?
相等。两者Class一致。
在类编译的时候,才体现出两者的差别,此时会进行泛型检查
10.序列化
1.Java序列化与反序列化是什么?
序列化:将java对象转化为字节序列的过程,以便在网络上进行传输。对象保存在JVM中,当JVM停止工作时,将对象序列化为二进制流,保存在文件中,当再次需要时,读取二进制文件反序列化获得对象。
反序列化:将字节序列转化为java对象的过程,客户端从文件中或网络上获得序列化后的对象字节流,根据字节流中所保存的对象状态及描述信息,通过反序列化重建对象。
2.为什么需要序列化和反序列化?
原因:需要对对象进行持久化和网络传输
详细原因:
-
对象序列化可以实现分布式对象
主要应用例如:RMI(即远程调用Remote Method Invocation)要利用对象序列化运行远程主机上的服务,就像在本地机上运行对象时一样
-
java对象序列化不仅保留一个对象的数据,还递归保留对象引用的每个对象的数据
保存整个对象层次,进行深度复制
-
序列化可以将内存中的对象写入到文件或者数据库中
-
对象,数据,文件保存格式不统一,通过序列化为二进制,方便保存
3.序列化实现方式有哪些?
实现Serializable接口或者Externalizable接口。
实现Serializable接口:原始接口,继承即可,不用实现方法
实现Externalizable接口:Externalizable
继承自Serializable
,该接口中定义了两个抽象方法:writeExternal()
与
readExternal()
,两个抽象需要重写,否则对象变量为默认值
两种序列化的对比
实现Serializable接口 | 实现Externalizable接口 |
---|---|
系统自动存储必要的信息 | 程序员决定存储哪些信息 |
Java内建支持,易于实现,只需要实现该接口即可,无需任何代码支持 | 必须实现接口内的两个方法 |
性能略差 | 性能略好 |
4.什么是serialVersionUID?
serialVersionUID用来验证对象版本一致性的。反序列化时,会讲serialVersionUID与本地相应类的serialVersionUID进行比较,不一致则报版本不一致异常。
5.为什么还要显示指定serialVersionUID的值?
如果不指定serialVersionUID的值,在序列化和反序列化时,JVM都会自动生成一个serialVersionUID,进行持久化或者网络传输时,反序列化的serialVersionUID会与序列化时的serialVersionUID不同而导致异常。
指定serialVersionUID可以使该对象的序列化和反序列化的serialVersionUID保持一致。
6.serialVersionUID什么时候修改?
不修改,除非出现版本异常
7.Java 序列化中如果有些字段不想进行序列化,怎么办?
使用transient关键字修饰变量(不可以修饰类和方法),可以使一些字段(变量)避免序列化,值为初始值
8.静态变量会被序列化吗?
不会,静态变量优于对象存在,随着类的加载而加载,而序列化是针对对象的
11.异常
1. Error 和 Exception 区别是什么?
Java 中,所有的异常都有一个共同的祖先 java.lang
包中的 Throwable
类。Throwable
类有两个重要的子类 Exception
(异常)和 Error
(错误)。
Exception
和 Error
二者都是 Java 异常处理的重要子类,各自都包含大量子类。
-
Exception
:程序本身可以处理的异常,可以通过catch
来进行捕获,通常遇到这种错误,应对其进行处理,使应用程序可以继续正常运行。Exception
又可以分为运行时异常(RuntimeException, 又叫非受检查异常)和非运行时异常(又叫受检查异常) 。 -
Error
:Error
属于程序无法处理的错误 ,我们没办法通过catch
来进行捕获 。例如,系统崩溃,内存不足,堆栈溢出等,编译器不会对这类错误进行检测,一旦这类错误发生,通常应用程序会被终止,仅靠应用程序本身无法恢复。
2.运行时异常(非受检查异)常和一般异常(受检查异常)的区别
运作时异常包括(非受检查异):RuntimeException及其子类:表示运行期间会出现的异常
- 包括
NullPointException(空指针)
、NumberFormatException(字符串转换为数字)
、IndexOutOfBoundsException(数组越界)
、ClassCastException(类转换异常)
、ArrayStoreException(数据存储异常,操作数组时类型不一致)
等。
一般异常(受检查异常):包含IO 异常、ClassNotFoundException
、SQLException
等
3.throw 和 throws 的区别是什么?
throw :在方法内部使用,只能抛出一个异常
throws:在方法声明上使用,可以抛出多个异常
4.NoClassDefFoundError 和 ClassNotFoundException 区别?
NoClassDefFoundError :Error类型的异常,JVM 或 ClassLoader 尝试加载某类时在内存中找不到该类的定义,该动作发
生在运行期间,即编译时该类存在,但是在运行时却找不到了,可能是编译后被删除了等原因导致。
ClassNotFoundException :使用 Class.forName, ClassLoader.loadClass 或 ClassLoader.findSystemClass 动态加载类到内
存的时候,通过传入的类路径参数没有找到该类,就会抛出该异常;另一种抛出该异常的可能原因是某个类已经由一个类
加载器加载至内存中,另一个加载器又尝试去加载它
5.Java常见异常有哪些?
Error:
- 内存不足错误:OutOfMemoryError
- 堆栈溢出错误:*Error
- 实例化错误:InstantiationError
- 违法访问错误:IllegalAccessError
Exception:
受检查异常:一般异常
- 找不到类异常:ClassNotFoundException
- 属性不存在异常:NoSuchFieldException
- 方法不存在异常:NoSuchMethodException
不受检查异常:运行时异常
- 数组索引越界异常:ArrayIndexOutOfBoundsException
- 索引越界异常:IndexOutOfBoundsException
- 字符串索引越界异常:StringIndexOutOfBoundsException
- 空指针异常:NullPointerException
- 强制类型转换异常:ClassCastException
- 算术异常:ArithmeticException(除0)
6.try-catch-finally 中哪个部分可以省略?
catch可以省略,try处理运行时异常,try+catch处理运行时和一般异常,finally进行收尾
7.try-catch-finally 中,如果 catch 中 return 了,finally 还会执行吗?
会执行,finally在return之前执行
8.JVM 是如何处理异常的?
当一个方法发生异常,方法创建一个异常对象转交给jvm,异常对象包含异常的各种信息,这个过程叫:抛出异常
在抛出异常的过程中,可能有一系列的调用,这时有一个调用栈,jvm查看调用栈,找到抛出的处理异常的方法进行处理,没找到,则默认打印异常信息并终止程序。
12.IO
1.java的IO流分为几种?
- 按照流的方向:输入流(inputStream)和输出流(outputStream);
- 按照实现功能分:节点流(可以从或向一个特定的地方读写数据,如 FileReader)和处理流(是对一个已存在的流的连接和封装,通过所封装的流的功能调用实现数据读写, BufferedReader);
- 按照处理数据的单位: 字节流和字符流。分别由四个抽象类来表示(每种流包括输入和输出两种所以一共四个):InputStream,OutputStream,Reader,Writer。Java中其他多种多样变化的流均是由它们派生出来的。
2.字节流如何转为字符流?
字节输入流转字符输入流通过 InputStreamReader 实现,该类的构造函数可以传入 InputStream 对象。
字节输出流转字符输出流通过 OutputStreamWriter 实现,该类的构造函数可以传入 OutputStream 对象。
3.字符流与字节流的区别?
- 读写的时候字节流是按字节读写,字符流按字符读写。
- 字节流适合所有类型文件的数据传输,字符流只能传文本
- 在读写文件需要对内容按行处理,比如比较特定字符,处理某一行数据的时候一般会选择字符流。
- 只是读写文件,和文件内容无关时,一般选择字节流。
4.BIO、NIO、AIO的区别?
- BIO:同步并阻塞,在服务器中实现的模式为一个连接一个线程。也就是说,客户端有连接请求的时候,服务器就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销,当然这也可以通过线程池机制改善。BIO一般适用于连接数目小且固定的架构,这种方式对于服务器资源要求比较高,而且并发局限于应用中,是JDK1.4之前的唯一选择,但好在程序直观简单,易理解。
- NIO:同步并非阻塞,在服务器中实现的模式为一个请求一个线程,也就是说,客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到有连接IO请求时才会启动一个线程进行处理。NIO一般适用于连接数目多且连接比较短(轻操作)的架构,并发局限于应用中,编程比较复杂,从JDK1.4开始支持。
- AIO:异步并非阻塞,在服务器中实现的模式为一个有效请求一个线程,也就是说,客户端的IO请求都是通过操作系统先完成之后,再通知服务器应用去启动线程进行处理。AIO一般适用于连接数目多且连接比较长(重操作)的架构,充分调用操作系统参与并发操作,编程比较复杂,从JDK1.7开始支持。
5.Java IO都有哪些设计模式?
使用了适配器模式和装饰器模式
适配器模式:
Reader reader = new INputStreamReader(inputStream);
从字节流对象中获取字符流的对象。
把一个类的接口变换成客户端所期待的另一种接口,从而使原本因接口不匹配而无法在一起工作的两个类能够在一起工作