目录
1. String
1.1. 简述String、StringBuffer、StringBuilder的区别
String类字符串不可改变,实现了equals方法,每当对String进行操作的时候,总是会创建新的字符串,很耗资源。
StringBuffer类可以对字符串进行修改,没有实现equals方法。
在大多数实现中,StringBuilder比StringBuffer要快。两者的方法基本相同,StringBuilder非线程安全;StringBuffer方法上都加了synchronized,靠锁实现线程安全;String是不可变类,所有不可变类都是线程安全的。
1.2.String s = "Hello";s = s + " world!";这两行代码执行后,原始的String对象中的内容到底变了没有?
没有。因为String被设计成不可变(immutable)类,所以它的所有对象都是不可变对象。
1.3.是否可以继承String类?
String类是final类故不可以继承。
1.4.String s = new String("xyz");创建了几个String Object?
如果用过’xyz’:一个,常量”xyz”不管出现多少遍,都是取缓冲区中的那一个。
如果没用过’xyz’:两个,New String每写一遍,就创建一个新的对象。
1.5.String是最基本的数据类型吗? String在内存中如何保存?
不是,String表示的值使用char数组保存。
1.6.如何把一段逗号分割的字符串转换成一个数组,反过来呢
字符串转数组:String [] result = orgStr.split(“,”);
数组转字符串:String str2 = StringUtils.join(result, ",");需要引入Apache Commons组件中的个commons-lang.jar包 或使用append方法
1.7.下面这条语句共创建了多少个对象:String s="a"+"b"+"c"+"d";
创建了一个。
1.8.能不能自己写个类,也叫java.lang.String
可以,但在应用的时候,需要用自己的类加载器去加载,否则,系统的类加载器永远只是去加载jre.jar包中的那个java.lang.String。
1.9.如何将String转换为char
String是一系列字符,所以无法转换成一个单一的char,但可以调用toCharArray() 方法将字符串转成字符数组。
String str = "Java interview";
char[] chars = str.toCharArray();
1.10.如何将String转换为byte array,反过来呢?
使用String的getBytes()方法将String转成byte数组
public class StringToByteArray {
public static void main(String[] args) {
String str = "PANKAJ";
byte[] byteArr = str.getBytes();
System.out.println("String to byte array: " + Arrays.toString(byteArr));
}
}
使用String的构造方法 new String(byte[] arr) 将byte数据转为String
public class ByteArrayToString {
public static void main(String[] args) {
byte[] byteArray = { 'P', 'A', 'N', 'K', 'A', 'J' };
byte[] byteArray1 = { 80, 65, 78, 75, 65, 74 };
String str = new String(byteArray);
String str1 = new String(byteArray1);
System.out.println(str);
System.out.println(str1);
}
}
1.11.有一个64k的字符串,是放到堆上,还是放到栈上,为什么?
只有引用和基本数据类型是直接存在栈上。对象类型可能是在堆、方法区、常量池中。
1.12. 字符串反转,如 a1c3反转成3c1a
一:
String str = "a1c3";
for (int i = str.length()-1; i >= 0; i--) {
char c = str.charAt(i);
System.out.print(c);
}
二:
System.out.println(new StringBuilder("a1c3").reverse().toString());
1.13.String类的常用方法
转换成数组toCharArray()
获取指定位置的字符charAt()
将字符串变成一个byte数组getBytes()
取得一个字符串的长度length()
查找一个指定的字符串是否存在indexOf()
去掉字符串左右空格trim()
字符串的截取substring()
按照指定的字符串拆分字符split()
大小写转换toUpperCase(),toLowerCase()
判断是否以指定的字符串开头或者结尾startsWith() , endsWith()
两个String类型内容比较equals()
两个字符串不区分大小写进行比较equalsIgnoreCase()
将一个指定得到字符串替换成其他字符串replaceAll()
2. final, finally, finalize
2.1. final, finally, finalize的区别
final 用于声明属性,方法和类,分别表示属性不可变,方法不可覆盖,类不可继承。
finally是异常处理语句结构的一部分,表示总是执行。
finalize是Object类的一个方法,对象被垃圾收集器回收的时候会调用此方法。
2.2.try {}里有一个return语句,那么紧跟在这个try后的finally {}里的code会不会被执行,什么时候被执行,在return前还是后?
会在return中间执行。
2.3.下面的程序代码输出的结果是多少?
public class smallT{
public static void main(String args[]){
smallT t = new smallT();
int b = t.get();
System.out.println(b);
}
public int get(){
try{
return 1 ;
}
finally{
return 2 ;
}
}
}
返回的结果是2。
2.4.使用final关键字修饰一个变量时,是引用不能变,还是引用的对象不能变?
引用不能变,引用的对象能变。
3. Overload和Override
3.1. Overload和Override的区别
Overload(重载)表示同一个类中可以有多个名称相同的方法,但这些方法的参数列表各不相同(即参数个数或或顺序类型不同),与修饰符和返回值无关。
Override(重写)子类把父类中定义的那个完全相同的方法给覆盖,返回值类型,参数列表必须相同,子类该方法权限要大于父类权限。
3.2. 构造器Constructor能否被重写
构造器Constructor不能被继承,因此不能重写,但可以被重载。
4. abstract 和interface
4.1.*abstract class和interface的区别
抽象类可以有普通成员变量、构造方法、非抽象的普通方法、静态方法,接口中则不能有。
抽象类中的抽象方法的访问类型可以不是public,接口中的抽象方法只能是public类型的。
一个类可以实现多个接口,但只能继承一个抽象类。
4.2.接口是否可继承接口? 抽象类是否可实现(implements)接口? 抽象类是否可继承具体类(concrete class)? 抽象类中是否可以有静态的main方法?
接口可以继承接口。
抽象类可以实现(implements)接口。
抽象类是可以继承具体类,但前提是具体类必须有明确的构造函数。
抽象类中可以有静态的main方法。
4.3.JAVA为什么需要接口?
接口弥补了java单继承的缺点。
4.4.abstract class和interface的应用场景
interface的应用场合:
1.类与类之前需要特定的接口进行协调,而不在乎其如何实现。
2.作为能够实现特定功能的标识存在,也可以是什么接口方法都没有的纯粹标识。
3.需要将一组类视为单一的类,而调用者只通过接口来与这组类发生联系。
4.需要实现特定的多项功能,而这些功能之间可能完全没有任何联系。
abstract class的应用场合
1.定义了一组接口,但又不想强迫每个实现类都必须实现所有的接口。可以用abstract class定义一组方法体,甚至可以是空方法体,然后由子类选择自己所感兴趣的方法来覆盖。
2.某些场合下,只靠纯粹的接口不能满足类与类之间的协调,还必需类中表示状态的变量来区别不同的关系。abstract的中介作用可以很好地满足这一点。
3.规范了一组相互协调的方法,其中一些方法是共同的,与状态无关的,可以共享的,无需子类分别实现;而另一些方法却需要各个子类根据自己特定的状态来实现特定的功能。
当你关注一个事物的本质的时候,用抽象类;当你关注一个操作的时候,用接口。
接口的另一个重要的应用就是多态的实现(当然抽象类也可以实现多态,但是接口更加合适)。
抽象类的功能要远超过接口,但是,定义抽象类的代价高。因为高级语言来说(从实际设计上来说也是)每个类只能继承一个类。在这个类中,你必须继承或编写出其所有子类的所有共性。虽然接口在功能上会弱化许多,但是它只是针对一个动作的描述。而且你可以在一个类中同时实现多个接口。在设计阶段会降低难度的。
5. &和&&
5.1. &和&&的区别
&和&&表示逻辑与,运算符两边的表达式的结果都为true时,整个运算结果才为true,&&具有短路的功能,第一个表达式为false,则不再计算第二个表达式,&可能会抛出NullPointerException异常。条件操作(&&)只能操作布尔型的,而逻辑操作(&)不仅可以操作布尔型,而且可以操作数值型。
6. 基本数据类型
6.1.switch语句能否作用在byte上,能否作用在long上,能否作用在String上?
Byte,short,char,int,String(1.7) 枚举 及其包装类 可以作用
6.2.short s1 = 1; s1 = s1 + 1;有什么错? short s1 = 1; s1 += 1;有什么错?
s1+1运算时会自动提升表达式的类型,结果是int型,再赋值时报强制转换类型的错误。
+= 是java语言规定的运算符,java编译器会对它进行特殊处理,第二个没有错。
6.3.char型变量中能不能存贮一个中文汉字?为什么?
可以,char型变量是用来存储Unicode编码的字符的,unicode编码字符集中包含了汉字,特殊的汉字没有被包含在unicode编码字符集中就不能存贮。
6.4.用最有效率的方法算出2乘以8等於几?
2 << 3
6.5. Integer与int的区别
int 基本数据类型,默认值为0
Integer 封装类,默认值为null
6.6.Math.round(11.5)等於多少? Math.round(-11.5)等於多少?
ceil向上取整,floor向下取整,round四舍五入
Math.round(11.5)的结果为12, Math.round(-11.5)的结果为-11。
6.7.什么是隐式转换,什么是显式转换
显示转换就是类型强转,把一个大类型的数据强制赋值给小类型的数据;隐式转换就是大范围的变量能够接受小范围的数据;隐式转换和显式转换其实就是自动类型转换和强制类型转换。
6.8.针对浮点型数据运算出现的误差的问题,你怎么解决?
使用Bigdecimal类进行浮点型数据的运算。
7. 异常
7.1. 运行时异常与一般异常有何异同?
运行时异常表示程序运行时出现的异常,编程时不处理编译也能通过。编译型异常必须处理。
7.2. error和exception有什么区别?
error 是由系统产生的不可控制、程序员无法处理、恢复很困难的严重问题。比如内存溢出。Error表示是由JVM进行处理的,是JVM出错。
exception是设计或实现问题,要求程序员在应用程序级进行处理,使用try—catch进行处理。
7.3.Java中的异常处理机制的简单原理和应用。
异常必须try..catch处理或用throws声明继续抛给上层调用方法处理。
7.4.try catch finally中finally中能return吗?
不能return。如果finally中有return,try或catch中return后面的代码会执行,但最终返回的结果为finally中return的值,需要注意的是try或catch中return后面的代码会执行,只是存起来了,并没有返回,让finally捷足先登先返回了。
7.5. 请写出你最常见到的10个异常。
NullPointerException,ClassNotFoundException,ArrayIndexOutOfBoundsException,NoSuchMethodError(方法不存在) ,IndexOutOfBoundsException(索引越界),NumberFormatException(数字格式异常),SQLException,IOException ,IllegalArgumentException(方法参数错误 ),IllegalAccessException(无访问权限异常)
7.6.try,catch,finally, throws,throw
try用来指定一块预防所有异常的程序
catch子句紧跟在try块后面,捕获指定的异常的类型
finally为确保一段代码不管发生什么异常状况都要被执行
throw手动抛出异常
throws抛出方法异常
7.7.什么情况下需要自定义异常
Java虽然提供了丰富的异常处理类,但是在项目中还会经常使用自定义异常,其主要原因是Java提供的异常类在某些情况下还是不能满足实际需球。如:系统中有些错误是符合Java语法,但不符合业务逻辑。
8. 线程
8.1.线程的基本概念、线程的基本状态以及状态之间的关系
线程:被称为轻量级进程,是程序执行流的最小单元。一个进程中可以包含多个线程。
进程:是一段程序的执行过程,是系统进行资源分配和调度的基本单位。
线程的状态关系:
1)新建:新创建了一个线程对象。
2)可运行:调用了该对象的start()方法。该状态的线程位于可运行线程池中,等待被线程调度选中,获取 cpu 的使用权 。
3)运行状态:线程获得了cpu 时间片 ,执行程序代码。
4)阻塞状态:线程因为某种原因放弃了cpu 使用权,暂时停止运行。直到线程进入可运行状态。
5)死亡状态:线程run()、main() 方法执行结束,或者因异常退出了run()方法,则该线程结束生命周期。死亡的线程不可再次复生。
8.2.sleep() 和 wait()
sleep是线程的静态调用方法,当前线程进入睡眠,指定的时间过后,继续执行,sleep方法并不会释放锁。
wait是Object类的方法,使当前线程进入等待队列,释放占用资源对象的锁,直到调用notify或notifyAll时,等待的资源对象的线程才被唤醒。
8.3.启动一个线程是用run()还是start()? .
启动一个线程是用start(),run()方法是该线程所关联的执行代码。
8.4.同步有几种实现方法?
1)使用synchronized关键字修饰类或者代码块
2)wait/notifyAll 方式
3)使用Volatile关键字修饰变量
4)使用重入锁
5)使用局部变量实现线程同步
8.5.*多线程实现的方法,用什么关键字修饰同步方法? stop()和suspend()方法为何不推荐使用?
多线程实现方法:
1)继承Thread类,重写Run函数
2)无返回值的任务必须实现Runnable接口,重写Run函数
3)可返回值的任务必须实现Callable接口,重写Call函数
修饰同步方法关键字:synchronized
stop()会解除由线程获取的所有锁定,不安全。
调用suspend()的时候,目标线程会停下来,但却仍然持有在这之前获得的锁定。容易发生死锁。
8.6.同步和异步有何异同,在什么情况下分别使用他们?举例说明。
如果数据将在线程间共享。例如正在写的数据以后可能被另一个线程读到,或者正在读的数据可能已经被另一个线程写过了,那么这些数据就是共享数据,必须进行同步存取。
当应用程序在对象上调用了一个需要花费很长时间来执行的方法,并且不希望让程序等待方法的返回时,就应该使用异步编程,在很多情况下采用异步途径往往更有效率。
8.7.下面两个方法同步吗?
class Test{
synchronized static void sayHello3(){}
synchronized void getX(){}
}
不是同步的;静态函数直接由类名调用,用到的锁是Test的字节码文件,即Test.class;非静态函数,由这个类的实例进行调用,所以用到的锁是this
8.8.当一个线程进入一个对象的一个synchronized方法后,其它线程是否可进入此对象的其它方法?
该其它方法是非synchronized()方法,那么是可以访问的,如果是synchronized()方法,那么不能访问。如果这个方法内部调用了wait,则可以进入其他synchronized方法。
8.9.简述synchronized和java.util.concurrent.locks.Lock的异同?
Lock能完成synchronized所实现的所有功能 ,synchronized会自动释放锁,而Lock一定要求程序员手工释放,并且必须在finally从句中释放。
8.10.如何理解分布式锁?
由于在平时的工作中,线上服务器是分布式多台部署的,经常会面临解决分布式场景下数据一致性的问题,那么就要利用分布式锁来解决这些问题。
8.11.同步和异步有什么区别?
同步和异步最大的区别就在于。一个需要等待,一个不需要等待。同步可以避免出现死锁,读脏数据的发生,一般共享某一资源的时候用,如果每个人都有修改权限,同时修改一个文件,有可能使一个人读取另一个人已经删除的内容,就会出错,同步就会按顺序来修改。
8.12.什么是乐观锁,什么是悲观锁,两者的区别是什么?
数据的锁包括乐观锁和悲观锁。
悲观锁是指数据对外界修改持保守态度,在数据处理的整个过程中,数据处于锁定状态。它认为其他用户访问或改变你正在访问、更改的对象的概率很高;所以在你开始改变对象之前就已经将此对象进行锁定,直到你所做的更改提交才会释放锁。因此,悲观锁的并发访问性并不好。
乐观锁采用了更为宽松的加锁机制。它认为其他用户改变你正在更改的对象的概率很小;所以只有你准备提交所做的更改时才会将该对象锁定。乐观锁相对悲观锁,并发访问机制要较好。
悲观锁比乐观锁的锁定时间要长。悲观锁大多数情况下依靠的是数据库的锁机制实现;而乐观锁大多数是数据版本记录机制实现。
8.13.进程间通信有哪几种方式?
管道,命名管道,信号,信号量,消息队列,共享内存,内存映射,套接口。
8.14.操作系统什么情况下会死锁?产生死锁的四个条件
死锁是指多个进程在运行过程中因争夺资源而造成的一种僵局。
1、互斥条件(进程独占资源)
2、请求与保持(进程因请求资源而阻塞时,对已获得的资源保持不放)
3、不剥夺条件(进程已获得的资源,在末使用完之前,不能强行剥夺)
4、循环等待(若干进程之间形成一种头尾相接的循环等待资源关系)
8.15.线程池,作用
根据系统自身的环境情况,有效的限制执行线程的数量,使得运行效果达到最佳。线程池通过控制执行的线程的数量,超出数量的线程排队等候,等待有任务执行完毕,再从队列最前面取出任务执行。
作用:降低资源消耗;提高响应速度;提高线程的可管理性。如果不使用线程池,有可能造成系统创建大量同类线程而导致消耗完内存或者“过度切换”的问题。
8.16.什么是多线程环境下的伪共享?
当多线程修改互相独立的变量时,如果这些变量共享同一个缓存行,就会无意中影响彼此的性能。
8.17.BIO与NIO、AIO的区别
IO的方式通常分为几种:同步阻塞的BIO、同步非阻塞的NIO、异步非阻塞的AIO。
BIO是一个连接一个线程。BIO方式适用于连接数目比较小且固定的架构,JDK1.4以前的唯一选择。
NIO是一个请求一个线程。NIO方式适用于连接数目多且连接比较短(轻操作)的架构,JDK1.4开始支持。
AIO是一个有效请求一个线程AIO方式使用于连接数目多且连接比较长(重操作)的架构,JDK7开始支持。
8.18.进程和线程区别
一个程序至少有一个进程,一个进程至少有一个线程。
进程有自己独立的地址空间,而线程共享进程的地址空间。
线程是处理器调度的基本单位,但进程不是。
二者均可并发执行,多线程比多进程成本低,但性能更低。
8.19.*Java提供的线程池有几种?
java.util.concurrent.Executors提供了一个 java.util.concurrent.Executor接口的实现用于创建线程池
1. newSingleThreadExecutor
单个线程的线程池,即线程池中每次只有一个线程工作,单线程串行执行任务
2. newFixedThreadExecutor(n)
固定数量的线程池,没提交一个任务就是一个线程,直到达到线程池的最大数量,然后后面进入等待队列直到前面的任务完成才继续执行
3. newCacheThreadExecutor(推荐使用)
可缓存线程池,当线程池大小超过了处理任务所需的线程,那么就会回收部分空闲(一般是60秒无执行)的线程,当有任务来时,又智能的添加新线程来执行。
4. newScheduleThreadExecutor
大小无限制的线程池,支持定时和周期性的执行线程
8.20.ThreadLocal的含义,应用场景
ThreadLocal是一个线程的局部变量,为解决多线程的并发问题,使用threadLocal可以很简洁的写出优美的多线程代码。
每个线程中都有一个自己的ThreadLocalMap类对象,可以将线程自己的对象保持到其中,各管各的,线程可以正确的访问到自己的对象。 将一个共用的ThreadLocal静态实例作为key,将不同对象的引用保存到不同线程的ThreadLocalMap中,然后在线程执行的各处通过这个静态ThreadLocal实例的get()方法取得自己线程保存的那个对象,避免了将这个对象作为参数传递的麻烦。
ThreadLocal是解决线程安全问题一个很好的思路,它通过为每个线程提供一个独立的变量副本解决了变量并发访问的冲突问题。在很多情况下,ThreadLocal比直接使用synchronized同步机制解决线程安全问题更简单,更方便,且结果程序拥有更高的并发性。最常见的ThreadLocal使用场景为用来解决数据库连接、Session管理等。
8.21.多线程的使用场景
为了防止主线程的阻塞,提高吞吐量,提高资源的使用率。
1、后台任务,例如:定时向大量(100w以上)的用户发送邮件;
2、异步处理,例如:发微博、记录日志等;
3、分布式计算
8.22.多线程优缺点
优点:
1.提高CPU的利用率。从磁盘上读取文件的时候,大多数的CPU时间都会花费在等待磁盘来读取数据。在这个时候CPU是相当空闲的。在这个时候它可以干点别的事情。通过改变操作的顺序,CPU可以得到更好的利用
2.防止阻塞主线程,提高吞吐量。使用线程可以把占据时间长的程序中的任务放到后台去处理
3.程序的运行效率可能会提高,提升程序的响应速度
缺点:
1.如果有大量的线程,会影响性能,因为线程的创建、切换、销毁都比较消耗系统资源
2.更多的线程需要更多的内存空间
3.线程中止需要考虑对程序运行的影响
4.通常块模型数据是在多个线程间共享的,需要防止线程安全问题、线程死锁情况的发生
9. 访问权限
9.1.请说出作用域public,private,protected,以及不写时的区别
作用域 | 当前类 | 同一package | 子孙类 | 其他package |
public | √ | √ | √ | √ |
protected | √ | √ | √ | × |
default | √ | √ | × | × |
private | √ | × | × | × |
10. equals
10.1."=="和equals方法究竟有什么区别?
基本类型用"=="比较两个变量的值,引用类型用"=="比较两个变量的地址。
基本类型里没有equals方法,引用类型equals用方法比较两个变量的地址;因为equals可以重写可以比较值,String重写equals方法所以比较的是两个字符串的内容。
10.2.两个对象值相同(x.equals(y) == true),但却可有不同的hash code,这句话对不对?
对。
11. 集合
11.1.介绍Collection框架的结构
Collection:3个子接口Set,List,Queue,Collection 是单列集合
Map:Hashtable,HashMap,TreeMap Map是一个双列集合
Set(集):元素无序的、不可重复,取出元素的方法只有迭代器。
Set接口中常用的类:
1>HashSet:线程不安全,存取速度快。保证元素唯一性依赖的是元素的hashCode方法和euqals方法。
2>TreeSet:线程不安全,可以对Set集合中的元素进行排序,默认自然升序排序。必须实现comparable接口,通过 compareTo或者compare方法中的来保证元素的唯一性。元素是以二叉树的形式存放的。
3>LinkedHashSet
List:元素是有序的、可重复
List接口中常用类:
1>Vector: 是内部是以动态数组的形式来存储数据的,线程安全,但速度慢,已被ArrayList替代。底层数据结构是数组结 构,当存储空间不足的时候,Vector默认增加为原来的一倍
2>ArrayList:线程不安全,查询速度快。底层数据结构是数组结构,ArrayList默认增加为原来的50%
3>LinkedList:是一个双链表,线程不安全。增删速度快。底层数据结构是列表结构
Map
1>Hashtable:线程安全,效率低。底层是哈希表数据结构。是同步的。key不可以重复,value可以重复,不允许null作为 键,null作为值。
2>HashMap:线程不安全,效率高。底层是哈希表数据结构。是不同步的。允许null作为键,null作为值。替代了Hashtable.
3>LinkedHashMap: 可以保证HashMap集合有序。存入的顺序和取出的顺序一致。
4>TreeMap:线程非安全,不允许空值,key不允许重复,value可以重复,treeMap迭代输出有序.
5>Properties:用于配置文件的定义和操作,使用频率非常高,同时键和值都是字符串。是集合中可以和IO技术相结合的对 象。
11.2.Collection框架中实现比较要实现什么接口
comparable/comparator
11.3.ArrayList和Vector的区别
1) Vector是线程安全的,而ArrayList不是。导致Vector效率无法和ArrayList相比;
2) ArrayList和Vector都采用线性连续存储空间,当存储空间不足的时候,ArrayList默认增加为原来的50%,Vector默认增加 为原来的一倍。
11.4.HashMap和Hashtable的区别
HashMap允许将null作为一个entry的key或者value,而Hashtable不允许。
HashMap非线程安全,效率要高于Hashtable。Hashtable线程安全。
Hashtable继承自Dictionary类,而HashMap是Java1.2引进的Map interface的一个实现。
Hashtable的方法是Synchronize的,而HashMap不是。
11.5.List 和 Map 区别?
List是存储单列数据的集合,存储的数据是有顺序,并且允许重复;
Map是存储键和值这样的双列数据的集合,存储的数据是没有顺序的,其键是不能重复的,它的值是可以有重复的。
11.6.ArrayList和linkedlist的区别
区别:
ArrayList是实现了基于动态数组的数据结构,LinkedList是基于链表结构。对于随机访问的get和set方法,ArrayList要优于 LinkedList,因为LinkedList要移动指针。对于新增和删除操作add和remove,LinkedList比较占优势,因为ArrayList要移动数据。
优缺点:
对ArrayList和LinkedList而言,在列表末尾增加一个元素所花的开销都是固定的。对ArrayList而言,主要是在内部数组中增加一项,指向所添加的元素,偶尔可能会导致对数组重新进行分配;而对LinkedList而言,这个开销是统一的,分配一个内部Entry对象。
在ArrayList集合中添加或者删除一个元素时,当前的列表所所有的元素都会被移动。而LinkedList集合中添加或者删除一个元素的开销是固定的。
LinkedList集合不支持高效的随机随机访问(RandomAccess),因为可能产生二次项的行为。ArrayList的空间浪费主要体现在在list列表的结尾预留一定的容量空间,而LinkedList的空间花费则体现在它的每一个元素都需要消耗相当的空间。
11.7.List、Map、Set三个接口,存取元素时,各有什么特点?
List与Set都是单列元素的集合,它们有一个功共同的父接口,叫Collection。
Set里面不允许有重复的元素,Set取元素时,没法说取第几个,只能以Iterator接口取得所有的元素,再逐一遍历各个元素。
List表示有先后顺序的集合,允许有重复的元素,加入对象按先来后到的顺序排序,调用add(int index,Obj)方法可以插队,可以以Iterator接口取得所有的元素,再逐一遍历各个元素之外,还可以调用get(index i)来明确说明取第几个。
Map是双列的集合,put方法存储,要存储一对key/value,不能存储重复的key,取则可以根据key获得相应的value,get(Object key)返回值为key 取得对应的value。
11.8.说出ArrayList,Vector, LinkedList的存储性能和特性
ArrayList和Vector都是使用数组方式存储数据,索引数据快而插入数据慢,Vector使用了synchronized方法,线程安全,通常性能上较ArrayList差。
LinkedList使用双向链表实现存储,线程不安全,插入速度较快。
11.9.去掉一个Vector集合中重复的元素
方法一
Vector newVector = new Vector();
For (int i=0;i<vector.size();i++){
Object obj = vector.get(i);
if(!newVector.contains(obj){
newVector.add(obj);
}
}
方法二
HashSet set = new HashSet(vector);
11.10.Collection 和 Collections的区别。
Collection是集合类的上级接口,继承与他的接口主要有Set和List。
Collections是针对集合类的一个帮助类,提供一系列静态方法实现对各种集合的搜索、排序、线程安全化等操作。
11.11.Set里的元素是不能重复的,那么用什么方法来区分重复与否呢? 是用==还是equals()? 它们有何区别?
Set里的元素是不能重复的,使用hashCode()、equals()方法进行判断的。
equals()和==方法决定引用值是否指向同一对象equals()在类中被覆盖,为的是当两个分离的对象的内容和类型相配的话,返回真值。
11.12.你所知道的集合类都有哪些?主要方法?
最常用的集合类是 List 和 Map。List 适用于按数值索引访问元素的情形;Map 集合类用于存储元素对,其中每个键映射到一个值。
set:add,remove,contains
map:put,remove,contains
set和list类都有一个iterator方法,用于返回iterator对象,map可以返回所有的key的集合,所有value的集合,或者key和value组合成的EntrySet对象的集合。
11.13.TreeSet里面放对象,如果同时放入了父类和子类的实例对象,那比较时使用的是父类的compareTo方法,还是使用的子类的compareTo方法,还是抛异常!
当前的add方法放入的是哪个对象,就调用哪个对象的compareTo方法。
11.14.Java 中的 TreeMap 是采用什么树实现的?
Java 中的 TreeMap 是使用红黑树实现的。
11.15.写一段代码在遍历 ArrayList 时移除一个元素,使用迭代器。
Iterator itr = list.iterator();
while(itr.hasNext()) {if(...) { itr.remove();} }
11.16.Comparable 和Comparator区别
Comparable内部比较器,Comparator外部比较器。
11.17.hashmap工作原理
hashmap是数组加链表实现的,存储的时候,根据hash值来存到数组中,当hash值一样的时候,会在数组的对应位置扩展一个链表,当链表长度大于10的时候,采用红黑树。
11.18.list去重
新建一个list数组:
List list = new ArrayList();
list.add(26);
list.add(39);
list.add(5);
list.add(40);
list.add(39);
list.add(25);
list.add(39);
方法一:使用java8新特性stream进行List去重
List newList = list.stream().distinct().collect(Collectors.toList());
System.out.println(“java8新特性stream去重:”+newList);
方法二:双重for循环去重
for (int i = 0; i < list.size(); i++) {
for (int j = 0; j < list.size(); j++) {
if(i!=j&&list.get(i)==list.get(j)) {
list.remove(list.get(j));
}
}
}
System.out.println(“双重for循环去重:”+list);
方法三:set集合判断去重,不打乱顺序
Set set1 = new HashSet();
List newList1 = new ArrayList();
for (Integer integer : list) {
if(set1.add(integer)) {
newList1.add(integer);
}
}
System.out.println(“set集合判断去重:”+list);
方法四:遍历后判断赋给另一个list集合
List newList2 = new ArrayList();
for (Integer integer : list) {
if(!newList2.contains(integer)){
newList2.add(integer);
}
}
System.out.println(“赋值新list去重:”+newList2);
方法五:set和list转换去重
Set set2 = new HashSet();
List newList3 = new ArrayList();
set2.addAll(list);
newList3.addAll(set2);
System.out.println(“set和list转换去重:”+newList3);
11.19.Hashmap使用时,是否考虑重写equals()或hashcode()?
hashmap存储时,先计算hashcode值,如果相同,在计算equals,相同则表示重复。
重复的数据,后者的值会覆盖前者。
使用HashMap,如果key是自定义的类,new出来的对象内存地址不同,就必须重写hashcode()和equals()。
如果重载了equals,比如说是基于对象的内容实现的,而保留hashCode的实现不变,那么很可能某两个对象明明是“相等”,而hashCode却不一样。这样,当用其中的一个作为键保存到hashMap、hasoTable或hashSet中,再以“相等的”找另一个作为键值去查找他们的时候,则根本找不到。对于每一个对象,通过其hashCode()方法可为其生成一个整形值(散列码),该整型值被处理后,将会作为数组下标,存放该对象所对应的Entry(存放该对象及其对应值)。 equals()方法则是在HashMap中插入值或查询时会使用到。当HashMap中插入值或查询值对应的散列码与数组中的散列码相等时,则会通过equals方法比较key值是否相等,所以想以自建对象作为HashMap的key,必须重写该对象继承object的hashCode和equals方法。
11.20. 如何确保Java的集合不被修改
添加Collections.unmodifiableList()或者Collections.unmodifiableMap()方法。
如果修改集合会报异常java.lang.UnsupportedOperationException。
例:
private static Map<Integer, String> map = new HashMap<>();
private static List<String> list = new ArrayList<>();
static {
map.put(1,"one");
map.put(1,"two");
map = Collections.unmodifiableMap(map);
}
static {
list.add("1");
list.add("2");
list = Collections.unmodifiableList(list);
}
11.21. HashMap中put get的过程描述
Get过程:根据key计算hashcode值,来定位数组位置,如该位置是链表,则通过equals来判断key值,key值相等,则返回该key的值。
Put过程:计算key的hashcode值,来决定存储位置,如果该位置有值,就使用equals来比较key值,如果key相等,则覆盖原值,如果不相等,则把新值插入到链表第一个元素。
11.22. TreeMap与HashMap区别
HashMap:基于哈希表实现,通过hashcode对其内容进行快速查找,元素的排列顺序不固定。适用于在Map中插入、删除和定位元素
TreeMap:基于红黑树实现,所有的元素都保持着某种固定的顺序。适用于按自然顺序或自定义顺序遍历键(key)。
HashMap 非线程安全,TreeMap 非线程安全。
HashMap通常比TreeMap快一点,在需要排序的Map时候才用TreeMap。
11.23. 迭代器 Iterator 是什么?怎么使用?有什么特点?
迭代器是一个对象,它的工作是遍历并选择序列中的对象。
迭代器通常被称为轻量级对象,创建它的代价小,程序员不需要知道该序列底层的结构,java的Iterator只能单向移动
Iterator可以用来:
使用方法iterator()要求容器返回一个Iterator。Iterator将准备好返回序列的第一个元素;
使用next()获取序列中下一个元素;
使用hasNext()检查序列中是否有元素;
使用remove()将迭代器新返回的元素删除。
11.24. Iterator 和 ListIterator 的区别
1. ListIterator有add()方法,可以向List中添加对象,而Iterator不能
2. ListIterator和Iterator都有hasNext()和next()方法,可以实现顺序向后遍历,但是ListIterator有hasPrevious()和previous()方法,可以实现逆向(顺序向前)遍历。Iterator就不可以。
3. ListIterator可以定位当前的索引位置,nextIndex()和previousIndex()可以实现。Iterator没有此功能。
4. 都可实现删除对象,但是ListIterator可以实现对象的修改,set()方法可以实现。Iierator仅能遍历,不能修改。
因为ListIterator的这些功能,可以实现对LinkedList等List数据结构的操作。
11.25. Queue接口中add()/offer()、remove()/poll()、element()/peek()的区别
add() 和 offer()
add() : 添加元素,如果添加成功则返回true,如果队列是满的,则抛出异常
offer() : 添加元素,如果添加成功则返回true,如果队列是满的,则返回false
区别:对于一些有容量限制的队列,当队列满的时候,用add()方法添加元素,则会抛出异常,用offer()添加元素,则返回 false
remove() 和 poll()
remove() : 移除队列头的元素并且返回,如果队列为空则抛出异常
poll() : 移除队列头的元素并且返回,如果队列为空则返回null
区别:在移除队列头元素时,当队列为空的时候,用remove()方法会抛出异常,用poll()方法则会返回null
element() 和 peek()
element() :返回队列头元素但不移除,如果队列为空,则抛出异常
peek() :返回队列头元素但不移除,如果队列为空,则返回null
区别 :在取出队列头元素时,如果队列为空,用element()方法则会抛出异常,用peek()方法则会返回null
11.26. 哪些集合类是线程安全的
Vector:比Arraylist多了个同步化机制(线程安全)。
Hashtable:比Hashmap多了个线程安全。
ConcurrentHashMap:是一种高效但是线程安全的集合。
Stack:栈,也是线程安全的,继承于Vector。
12. IO
12.1.字节流与字符流的区别(从继承,基本单元,缓冲区,处理数据方面来讲)
字节流继承于InputStream和OutputStream,字符流继承于Reader 和Writer。
字节流操作的基本单元为字节;字符流操作的基本单元为Unicode码元。
字节流默认不使用缓冲区;字符流使用缓冲区。
字节流通常用于处理二进制数据,实际上它可以处理任意类型的数据,但它不支持直接写入或读取Unicode码元;字符流通常处理文本数据,支持写入和读取Unicode码元。
12.2.java中有几种类型的流?JDK为每种类型的流提供了一些抽象类以供继承,请说出他们分别是哪些类?
字节流,字符流。字节流继承于InputStream和OutputStream,字符流继承于Reader 和Writer。
12.3.什么是java序列化,如何实现java序列化?或者请解释Serializable接口的作用
将 Java 对象转换成字节流的过程。
传输的对象必须实现serializable接口。
Serializable是java提供的通用数据保存和读取的接口,序列化作用为了反序列化。
12.4. 什么情况下需要序列化?
当 Java 对象需要在网络上传输或者持久化存储到文件中时,就需要对 Java 对象进行序列化处理。
13. JVM
13.1.描述一下JVM加载class文件的原理机制?
当运行指定程序时,由类加载器判断哪些.class需要加载,把需要加载的class文件加载到虚拟机5个内存区域。
13.2.堆(heap)和栈(stack)有什么区别。
Java自动管理栈和堆,程序员不能直接地设置栈或堆。
栈由操作系统自动分配释放,使用的是一级缓存,调用完毕立即释放,栈是一种先进后出的数据结构优势是,存取速度比堆要快。
堆一般由程序员分配释放,是存放在二级缓存中,生命周期由虚拟机的垃圾回收算法来决定,堆可以被看成是一棵树,优势是可以动态地分配内存大小。
JVM 中堆和栈属于不同的内存区域,使用目的也不同。栈常用于保存方法帧和局部变量,而对象总是在堆上分配。栈通常都比堆小,也不会在多个线程之间共享,而堆被整个 JVM 的所有线程共享。
13.3.GC是什么? 为什么要有GC?
GC是垃圾收集的意思。
Java提供的GC功能可以自动监测对象是否超过作用域从而达到自动回收内存的目的。
13.4.垃圾回收的优点和原理。并考虑2种回收机制
Java程序员在编写程序的时候不再需要考虑内存管理,可以有效的防止内存泄露,有效的使用可以使用的内存,程序更加安全更加健壮。
垃圾回收器不可预知的情况下对内存堆中已经死亡的或者长时间没有使用的对象进行清除和回收,程序员不能实时的调用垃圾回收器对某个对象或所有对象进行垃圾回收。
回收机制有分代复制垃圾回收和标记垃圾回收,增量垃圾回收。
13.5.垃圾回收器的基本原理是什么?垃圾回收器可以马上回收内存吗?有什么办法主动通知虚拟机进行垃圾回收?
创建对象时,GC就开始监控这个对象的地址、大小以及使用情况。GC采用有向图的方式记录和管理堆(heap)中的所有对象。
不保证GC一定会执行。
可以手动执行System.gc()
13.6.GC算法(什么样的对象算是可回收对象,可达性分析)
jvm判断一个对象已经变成了可回收的“垃圾”,一般是两个方法:引用记数法和根搜索算法。引用记数法是给对象中添加一个引用计数器,每当有一个地方引用它时,计数器就加1;当引用失效时,计数器值就减1;任何时刻计数器都为0的对象就是不可能再被使用的。Java语言没有选用引用计数法来管理内存,因为引用计数法不能很好的解决循环引用的问题,所以用根搜索,在主流的商用语言中,都是使用根搜索算法来判定对象是否存活的。从一系列的”GC Roots“对象开始向下搜索,搜索走过的路径称为引用链。当一个对象到”GC Roots“之间没有引用链时,被称为引用不可达。引用不可到的对象被认为是可回收的对象。
13.7.GC在什么时候,对什么东西,做了什么事情
< 在什么时候 > GC又分为minor GC和Full Gc,Java堆内存分为新生代和老年代,新生代中又分为1个Eden区域和两个Survivor区域。对于Minor GC的,如果 Eden区域没有足够的空间,那么就会发起一次 Minor GC;对于Full GC,如果老年代没有足够空间的话,那么就会进行一次Full GC。但是,具体到什么时刻执行,这个是由系统来进行决定,是无法预测的。
< 对什么东西 > GC算法(13.6)
< 做什么事情 > 主要做了清理对象,整理内存的工作。Java堆分为新生代和老年代,采用了不同的回收方式。例如新生代采用了复制算法,老年代采用了标记整理法。在新生代中,分为一个Eden 区域和两个Survivor区域,真正使用的是一个Eden区域和一个Survivor区域,GC的时候,会把存活的对象放入到另外一个Survivor区域中,然后再把这个Eden区域和Survivor区域清除。那么对于老年代,采用的是标记整理法,首先标记出存活的对象,然后再移动到一端。这样也有利于减少内存碎片。
13.8.JVM分为哪些区,每一个区干吗的?(jvm的内存结构)
本地方法区(method):被所有的线程共享。方法区包含所有的类信息和静态变量。
堆(heap):被所有的线程共享,存放对象实例以及数组,Java堆是GC的主要区域。
本地方法栈、虚拟机栈:每个线程包含一个栈区,栈中保存一些局部变量等。
程序计数器:是当前线程执行的字节码的行指示器。
13.9.JVM新生代,老年代,持久代,都存储哪些东西?
持久代主要存放的是Java类的类信息,与垃圾收集要收集的Java对象关系不大。所有新生成的对象首先都是放在年轻代的,年老代中存放的都是一些生命周期较长的对象。
13.10.java中会存在内存泄漏吗
内存泄露就是指一个不再被程序使用的对象或变量一直被占据在内存中。java中有垃圾回收机制,它可以保证一对象不再被引用的时候,对象将自动被垃圾回收器从内存中清除掉。长生命周期的对象持有短生命周期对象的引用就很可能发生内存泄露。
13.11.内存溢出和内存泄漏
内存溢出:程序申请内存时,没有足够的内存,out of memory;
内存泄漏值垃圾对象无法回收,可以使用memory analyzer工具查看泄漏。
13.12.什么是驻留
JVM加载类时,会将所有的字面量保存在一个常量池里,如果出现了重复的String字面量,那么重复字面量可以通过池中已经存在的相同常量来引用。
13.13.Class.forName的作用?为什么要用?
Class.forName() 返回的是一个类,java里面任何class都要装载在虚拟机上才能运行,这句话就是装载类用的。在调用class的静态加载方法forName时获得更好的灵活性,提供给了一种降耦的手段。
13.14.private static final long serialVersionUID = 1L;的作用
保持软件版本的兼容性,在版本升级时反序列化仍保持对象的唯一性。如果不加这一行,会造成反序列化失败,不同的JVM之间的序列化算法是不一样的,不利于程序的移植。
13.15. 强引用,软引用和弱引用的区别
强引用:
只有这个引用被释放之后,对象才会被释放掉,只要引用存在,垃圾回收器永远不会回收,这是最常见的New出来的对象。
软引用:
内存溢出之前通过代码回收的引用。软引用主要用户实现类似缓存的功能,在内存足够的情况下直接通过软引用取值,无需从繁忙的真实来源查询数据,提升速度;当内存不足时,自动删除这部分缓存数据,从真正的来源查询这些数据。
弱引用:
第二次垃圾回收时回收的引用,短时间内通过弱引用取对应的数据,可以取到,当执行过第二次垃圾回收时,将返回null。弱引用主要用于监控对象是否已经被垃圾回收器标记为即将回收的垃圾,可以通过弱引用的isEnQueued方法返回对象是否被垃圾回收器标记。
13.16. 双亲委派模型方法
Java 类加载器可以分为三种
1) 启动类加载器(Bootstrap Class-Loader),加载 jre/lib 包下面的 jar 文件。
2) 扩展类加载器(Extension or Ext Class-Loader),加载 jre/lib/ext 包下面的 jar 文件。
3) 应用类加载器(Application or App Clas-Loader),根据程序的类路径(classpath)来加载 Java 类。
以上三种类加载器不能满足要求的话,程序员还可以自定义类加载器它们之间的层级关系如下:
启动类加载器< ---扩展类加载器< ---应用类加载器< ---自定义类加载器
这种层次关系被称作为双亲委派模型:
一个类加载器收到了加载类的请求,它会先把请求委托给上层加载器去完成,上层加载器又会委托上上层加载器,一直到最顶层的类加载器;如果上层加载器无法完成类的加载工作时,当前类加载器才会尝试自己去加载这个类。
14. 设计模式
14.1.单例模式
单例模式是一种常用的软件设计模式,其定义是单例对象的类只能允许一个实例存在。
适用场景:
1.需要生成唯一序列的环境
2.需要频繁实例化然后销毁的对象。
3.创建对象时耗时过多或者耗资源过多,但又经常用到的对象。
4.方便资源相互通信的环境
主要实现的步骤:
1.将该类的构造方法定义为私有方法,这样其他处的代码就无法通过调用该类的构造方法来实例化该类的对象,只有通过该类提供的静态方法来得到该类的唯一实例;
2.在该类内提供一个静态方法,当我们调用这个方法时,如果类持有的引用不为空就返回这个引用,如果类保持的引用为空就创建该类的实例并将实例的引用赋予该类保持的引用。
优缺点:
优点:
1.在内存中只有一个对象,节省内存空间;
2.避免频繁的创建销毁对象,可以提高性能;
3.避免对共享资源的多重占用,简化访问;
4.为整个系统提供一个全局访问点。
缺点:
1.不适用于变化频繁的对象;
2.滥用单例将带来一些负面问题,如为了节省资源将数据库连接池对象设计为的单例类,可能会导致共享连接池对象的程序过多而出现连接池溢出;
3.如果实例化的对象长时间不被利用,系统会认为该对象是垃圾而被回收,这可能会导致对象状态的丢失;
懒汉模式:线程不安全,不支持多线程
public class Singleton {
private static Singleton instance;
private Singleton (){}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
懒汉模式:线程安全,支持多线程,效率低
public class Singleton {
private static Singleton instance;
private Singleton (){}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
饿汉模式:线程安全,效率高、类加载时就初始化,浪费内存、容易产生垃圾对象
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton (){}
public static Singleton getInstance() {
return instance;
}
}
双检锁/双重校验锁:线程安全,双锁机制,安全且在多线程情况下能保持高性能
public class Singleton {
private volatile static Singleton singleton;
private Singleton (){}
public static Singleton getSingleton() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
登记式/静态内部类:线程安全
public class Singleton {
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
private Singleton (){}
public static final Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
枚举:线程安全
public enum Singleton {
INSTANCE;
public void whateverMethod() {
}
}
14.2.常用的设计模式?说明工厂模式。
设计模式是一套被反复使用的代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。设计模式使人们可以更加简单方便的复用成功的设计和体系结构。
设计模式有三类(创建型、结构型、行为型)共23种设计模式。
Java中的23种设计模式:
Factory(工厂模式),Builder(建造模式),Factory Method(工厂方法模式),Prototype(原始模型模式),Singleton(单例模式),Facade(门面模式),Adapter(适配器模式),Bridge(桥梁模式),Composite(合成模式),Decorator(装饰模式), Flyweight(享元模式),Proxy(代理模式),Command(命令模式),Interpreter(解释器模式)Visitor(访问者模式),Iterator(迭代子模式),Mediator(调停者模式),Memento(备忘录模式),Observer(观察者模式),State(状态模式),Strategy(策略模式),Template Method(模板方法模式),Chain Of Responsibleity(责任链模式)
工厂模式是 Java 中最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。
15. 反射
15.1.什么是反射机制?作用?
反射机制指的是程序在运行时能够获取自身的信息。框架底层基本是由反射实现的,学习反射主要目的是加深对框架的理解,为了写更加具有通用性的代码。
在运行时判断任意一个对象所属的类;
在运行时获取类的对象;
在运行时访问java对象的属性,方法,构造方法等。
15.2.哪里用到反射机制
jdbc中生成驱动,很多框架都用到反射机制,hibernate,struts都是用反射机制实现的。
15.3.反射机制的优缺点
优点:可以实现动态创建对象和编译,体现出很大的灵活性。
缺点:对性能有影响,使用反射基本上是一种解释操作,这类操作总是慢于执行相同的操作。
15.4.Java反射创建对象效率高还是通过new创建对象的效率高
通过new创建对象的效率比较高。通过反射时,先找查找类资源,使用类加载器创建,过程比较繁琐,所以效率较低。
15.5. 动态代理是什么?应用场景?
动态代理:在运行时,创建目标类,可以调用和扩展目标类的方法。
应用场景:Spring的AOP,加事务,加权限,加日志。
16. Class
16.1. 创建对象的方式
New,clone,反射,序列化/反序列化
16.2. 获取class类的几种方法
Class.forName()
类名.class
包装类.TYPE
对象名.getClass()
Class类.getSuperClass()
16.3. 一个".java"源文件中是否可以包括多个类(不是内部类),有什么限制
可以有多个类,但只能有一个public的类,并且public的类名必须与文件名相一致。
16.4. 什么是内部类,Static Nested Class(嵌套类)和 Inner Class(内部类)的不同
内部类就是在一个类的内部定义的类,内部类与C++嵌套类最大的不同就在于是否有指向外部的引用上.
16.5. 内部类可以引用它的包含类的成员吗,有没有什么限制
如果不是静态内部类,可以引用它的包含类的成员. 静态内部类不能访问外部类的成员
16.6. Anonymous Inner Class (匿名内部类) 是否可以extends(继承)其它类,是否可以implements(实现)interface(接口)
必须继承其他类或实现其他接口
16.7. super.getClass()方法调用下面程序的输出结果是多少?
import java.util.Date;
public class Test extends Date{
public static void main(String[] args) {
new Test().test();
}
public void test(){
System.out.println(super.getClass().getName());
}
}
Test
16.8. *类加载过程
Java的类加载过程可以分为 5 个阶段:载入、验证、准备、解析和初始化。这5个阶段一般是顺序发生的,但在动态绑定的情况下,解析阶段发生在初始化阶段之后。
1)Loading(载入)
JVM 在该阶段的主要目的是将字节码从不同的数据源(可能是 class 文件、也可能是 jar 包,甚至网络)转化为二进制字节 流加载到内存中,并生成一个代表该类的 java.lang.Class 对象。
2)Verification(验证)
JVM 会在该阶段对二进制字节流进行校验,只有符合 JVM 字节码规范的才能被 JVM 正确执行,该阶段是保证 JVM 安全的 重要屏障。
下面是一些主要的检查:
确保二进制字节流格式符合预期(比如说是否以 cafe bene 开头)。
是否所有方法都遵守访问控制关键字的限定。
方法调用的参数个数和类型是否正确。
确保变量在使用之前被正确初始化了。
检查变量是否被赋予恰当类型的值。
3)Preparation(准备)
JVM 会在该阶段对类变量(也称为静态变量,static 关键字修饰的)分配内存并初始化(对应数据类型的默认初始值,如 0、0L、null、false 等)。
4)Resolution(解析)
该阶段将常量池中的符号引用转化为直接引用。
符号引用:以一组符号(任何形式的字面量,只要在使用时能够无歧义的定位到目标即可)来描述所引用的目标。
直接引用:通过对符号引用进行解析,找到引用的实际内存地址。
5)Initialization(初始化)
该阶段是类加载过程的最后一步。在准备阶段,类变量已经被赋过默认初始值,而在初始化阶段,类变量将被赋值为代码 期望赋的值。换句话说,初始化阶段是执行类构造器方法的过程。
16.9. 双亲委派模型方法
Java 类加载器可以分为三种
1) 启动类加载器(Bootstrap Class-Loader),加载 jre/lib 包下面的 jar 文件。
2) 扩展类加载器(Extension or Ext Class-Loader),加载 jre/lib/ext 包下面的 jar 文件。
3) 应用类加载器(Application or App Clas-Loader),根据程序的类路径(classpath)来加载 Java 类。
以上三种类加载器不能满足要求的话,程序员还可以自定义类加载器它们之间的层级关系如下:
启动类加载器< ---扩展类加载器< ---应用类加载器< ---自定义类加载器
这种层次关系被称作为双亲委派模型:
一个类加载器收到了加载类的请求,它会先把请求委托给上层加载器去完成,上层加载器又会委托上上层加载器,一直到最顶层的类加载器;如果上层加载器无法完成类的加载工作时,当前类加载器才会尝试自己去加载这个类。
17. 算法、数据结构
17.1.什么是数据结构?数据的逻辑结构?
数据结构是计算机保存,组织数据的方式
集合,线性结构,图结构,树结构
17.2.栈和队列特点
栈,后进先出
队列,先进先出
17.3.二分查找的使用范围
数据不经常变动,查找频繁的有序结构。
17.4.顺序存储结构和链式存储结构
顺序存储结构节省存储的空间,增加访问速度,插入删除元素时慢
链式存储结构占用空间大,查找慢,插入删除灵活
17.5.顺序查找
int search(int a[],int lenght, int b) {
int i;
for (i=0; i<lenght; i++) {
if(b==a[i])
return i; // 返回元素的下标
}
return -1; // 没有找到
}
17.6.二分查找
int binarySearch(int a[],int len, int x){
int mid; // 中间下标
int low=0; // 区间的左端下标
int high=len-1; // 区间的右端下标
while(low <= high) {
mid = low + (high-low)/2;
if(x==a[mid]){
return mid; // 若找到返回元素的下标
}else if(x>a[mid]){
low=mid+1; // 在右半边区间搜索
}else{
high=mid-1; } // 在左半边区间搜索
}
return -1; // 没有找到
}
17.7.冒泡排序
for(int i =0;i < score.length - 1;i++) {
for(int j = 0;j < score.length - 1-i;j++){
if(score[j] < score[j+1]) {
int temp = score[j];
score[j] = score[j+1];
score[j+1] = temp;
}
}
}
17.8.第1个人10,第2个比第1个人大2岁,依次递推,请用递归方式计算出第8个人多大?
public static int getComputeAge(int n) {
int age = 0;
if (n == 1) {
age = 10;
} else {
age = getComputeAge(n - 1) + 2;
}
return age;
}
18. 对象拷贝
18.1. 为什么要使用克隆
想对一个对象进行处理,又想保留原有的数据进行接下来的操作,就需要克隆。
18.2. 如何实现对象克隆
(1)实现Cloneable接口并重写Object类中的clone()方法;
(2)实现Serializable接口,通过对象的序列化和反序列化实现克隆,可以实现真正的深度克隆。
18.3. 深拷贝和浅拷贝
浅拷贝只是复制了对象的引用地址,两个对象指向同一个内存地址,所以修改其中任意的值,另一个值都会随之变化
深拷贝是将对象及值复制过来,两个对象修改其中任意的值另一个值不会改变
18.4. 写clone()方法时,通常都有一行代码,是什么?
clone 有缺省(默认)行为,super.clone();因为首先要把父类中的成员复制到位,然后才是复制自己的成员。
19. 其他
1. Java有没有goto?
java中的保留字,现在没有在java中使用。
2. 在JAVA中如何跳出当前的多重嵌套循环?
1)外面的循环语句前定义一个标号,然后在里层循环体的代码中使用带有标号的break 语句
2)让外层的循环条件表达式的结果可以受到里层循环体代码的控制
3. 静态变量和实例变量的区别?
静态变量前要加static关键字,而实例变量前则不加。
实例变量必须创建对象后才可以通过这个对象来使用,静态变量则可以直接使用类名来引用。
4. 是否可以从一个static方法内部发出对非static方法的调用?
不可以。非static方法必须创建一个对象后,才可以在该对象上进行方法调用,而static方法调用时不需要创建对象,可以直接调用。
5. 面向对象的特征有哪些方面
面向对象的编程语言有封装、继承 、抽象、多态等4个主要的特征。
6. java中实现多态的机制是什么?
父类引用子类对象。
7. 数组有没有length()这个方法? String有没有length()这个方法?
数组没有length()这个方法,有length的属性。String有length()这个方法。
8. java中有没有指针?
有指针,但是隐藏了,开发人员无法直接操作指针,由jvm来操作指针。
9. 实例化数组后,能不能改变数组长度呢?
不能,数组一旦实例化,它的长度就是固定的。
10. 泛型的好处
类型安全,消除强制类型转换,另一种代码重用机制。
11. BS与CS的联系与区别。
C/S 客户端服务器,安全高,重用性低,系统维护难。
B/S 浏览器服务器,安全低,重用性高,系统维护容易。
12. JDO(Java Date Objects)
是Java对象持久化的新规范,是一个用于存储某个数据库中对象的标准化API。JDO提供了透明的对象存储,它很灵活,可以在任何数据底层运行。
13. 详细解释一下IP地址的定义,在哪个层上面,主要有什么作用?TCP和UDP呢?
IP协议是网络层的协议,它是为了实现相互连接的计算机进行通信设计的协议,它实现了自动路由功能,即自动寻径功能。TCP是传输层的协议,它向下屏蔽IP协议的不可靠传输的特性,向上提供一种面向连接的、可靠的点到点数据传输。TCP在可靠性和安全性上等更有保证。UDP也是传输层协议,它提供的是一种非面向连接的,不可靠的数据传输,这主要是有些应用需要更快速的数据传输,比如局域网内的大多数文件传输都是基于UDP的。UDP在传输速率上更快,开销更小。
14. 简述一下面向对象的“六原则一法则”。
单一职责原则:一个类只做它该做的事情。
开闭原则:软件实体应当对扩展开放,对修改关闭。
依赖倒转原则:面向接口编程。
里氏替换原则:任何时候都可以用子类型替换掉父类型。
接口隔离原则:接口要小而专,绝不能大而全。
合成聚合复用原则:优先使用聚合或合作关系复用代码。
迪米特法则:又叫最少知识原则,一个对象应当对其他对象有尽可能少的了解。
15. 说出JAVA中一些常用的类,包,接口,请各举5个
类Object,String,System,File,Date,Integer,Class
包java.lang,java.io,java.util,java.sql,javax.servlet
接口List,Map,Iterator,CallableStatement,Comparable
16. 什么是hash冲突,解决办法。
就是根据key即经过一个函数f(key)得到的结果的作为地址去存放当前的key value键值对,但是却发现算出来的地址上已经有人先来了。
开放定址法,链地址法,再哈希法,建立一个公共溢出区。
17. 解释 Java 堆空间
当通过 Java 命令启动Java 进程的时候,会为它分配内存。内存的一部分用于创建堆空间,当程序中创建对象的时候,就从对空间中分配内存。
18. UML是什么,UML中有哪些常用的图
标准建模语言。
用例图、类图、时序图、协作图、状态图、活动图、构件图、部署图等。(前三种图最重要)