目录:
9.4、ArrayList和LinkedList的区别?分别用在什么场景?
9.9、HashMap是线程安全的吗?线程安全的Map都有哪些?性能最好的是哪个?
10、String、StringBuffer、StringBuilder有什么区别?
13.2、一个线程的生命周期有哪几种状态?它们之间如何流转的?
13.5、新建T1、T2、T3三个线程,如何保证它们按顺序执行?
13.9、Java中notify和notifyAll有什么区别?
14.1、UDP:面向无连接,速度快,不安全,区分发送端和接收端
14.2、TCP:面向连接,速度稍慢,安全,区分客户端和服务端
1、&和&&的区别?
相同点:执行的结果都是一样的
不同点:
&&具有短路效果,如果运算符左边的结果为false时,运算符右边不执行
&没有短路效果,不管运算符的左边的结果是false还是true,运算符右边一定会执行
同例:
==和 equals 的区别?
equals 和== 最大的区别是一个是方法一个是运算符。
- ==:如果比较的对象是基本数据类型,则比较的是数值是否相等;如果比较的是引用数据类型,则比较的是对象的地址值是否相等。
- equals():用来比较方法两个对象的内容是否相等。
注意:equals 方法不能用于基本数据类型的变量,如果没有对 equals 方法进行重写,则比较的是引用类型的变量所指向的对象的地址。
2、继承的概述及特点
2.1、继承的概述:
(1) 现实中的继承是儿子继承父辈,然后可以拥有父辈的财产。
(2) 在Java中的继承是子类继承父类,然后就可以拥有父类中非私有的成员。
(3)多个类中有共同的成员变量和成员方法,这时就向上抽取出相同的部分,定义到另一个类(父类)中,让多个类(子类)再去使用extends关键字继承父类,就拥有了父类中非私有的成员。
2.2、继承的优缺点:
(1)优点:
提高了代码的复用性、可维护性。
(2)缺点:
类的耦合性增强了。
开发的原则:高内聚低耦合
内聚:就是自己完成某件事情的能力
耦合:类与类的关系
2.3、继承的特点:
(1)Java中的类只支持单一继承,不支持多继承
classA{}
classB{}
classC extends A,B{}// 错误的
classC extends A{}// 对的
classC extends B{}// 对的
(2)Java中的类支持多层继承
classA{}
classB extends A{}
classC extends B{}
(3)Java中的类,一个类可以被多个类同时继承
classA{}
classB extends A{}
classC extends A{}
2.4、继承成员变量的特点:
子类只能获取父类非私有成员
子父类中成员变量的名字不一样直接获取父类的成员变量
子父类中成员变量名字是一样的获取的是子类的成员变量
2.4就近原则:谁离我近我就用谁。
如果有局部变量就使用局部变量
如果没有局部变量,有子类的成员变量就使用子类的成员变量
如果没有局部变量和子类的成员变量,有父类的成员变量就使用父类的成员变量, 啥都没有,出错了!!!
2.5继承中的构造方法的执行顺序
先执行父类的构造方法,再执行子类的构造方法,因为每一个构造方法的第一行默认都有一个super(),调用的是父类的空参构造。
如果手动显示在构造方法的第一行使用super(参数)或者this(参数),就没有默认的super(),调用指定的构造方法。
3、this和super的区别
this:当前对象的引用,谁来调用我,谁就代表当前对象
(1)调用子类的成员变量和成员方法
(2)在子类的构造方法第一行调用子类其他的构造方法,使用this()
super:父类的那片空间
(1)调用父类的成员变量和成员方法
(2)在子类中的构造方法第一行调用父类的构造方法,使用super()
4、方法重写和方法重载的区别
重写:重写 Overriding 是父类与子类之间多态性的一种表现
在子父类中,子类重新定义了父类的方法。方法重写必须有相同的方法名,方法声明相同,方法体不同。参数列表和返回类
重载:重载 Overloading 是一个类中多态性的一种表现
在一个类中,有多个重名的的方法,但是其参数不一样(参数的个数,参数的类型,参数的顺序),和返回值无关。
5、接口与抽象类的区别
(1)定义上
接口:比抽象类还要抽象的类,用interface关键字定义
抽象类:有抽象方法的类必须是抽象类
(2)成员上
成员变量(常量):
抽象类可以有成员变量,也可以有常量
接口只能有常量
成员方法:
抽象类可以有抽象方法,可以有非抽象方法
接口只能有抽象方法,而且方法有默认修饰符 public static
构造方法:
抽象类有构造方法的
接口没有构造方法
(3)与类之间的关系
类与接口是实现的关系,而且是多实现,一个类可以实现多个接口。
类与抽象类是继承关系,java中的继承是单一继承,一个类只能有一个父亲,java中的继承是多层继承。
6、多态的前提和特点
(1)多态的前提:
有继承或实现关系;
一般有重写方法;
有父类引用指向子类的关系 。
动态绑定:运行期间调用的方法,是根据其具体的类型
(2)多态的成员特点:
编译时看的都是左边,运行时成员方法看的是右边,其他(成员变量和静态的方法)看的都是右边
(3)多态引用数据类型的转换:
向上转型:子类类型提升父类类型(父类引用指向子类对象)
Animala = new Dog();
向下转型:父类类型转换为子类类型
Dogd = (Dog)a;
多态的弊端:多态之后不能再调用子类特有的成员
(4)多态的好处:提高了程序的扩展性
注:instanceof运算符:用于判断对象是否是指定类型
7、权限修饰符
四种:public > proctected > 默认 > private
public 当前类 , 相同包下的类 不同包下的类
default当前类 , 相同包下的类
private当前类
protected当前类 , 相同包下的类
使用:
default:当前包下使用
protected:让子类对象调用
private:修饰的成员只能在本类中使用
public:修饰的成员在本项目中都可以使用
8. 集合的遍历方式
方式一:使用普通for循环,size()和get(int index)结合【只适用于List集合】
//创建集合对象
ArrayList list = new ArrayList();
//添加元素
list.add("a");
list.add("b");
list.add("c");
//遍历
for(int i=0;i<list.size();i++) {
System.out.println(list.get(i));
}
方式二:使用普通for,toArray()方法先将集合转为数组,然后遍历数组
Collection c = new ArrayList();
c.add("a");
c.add("b");
c.add("c");
//遍历
Object[]objs = c.toArray();
for(inti=0;i<objs.length;i++){
System.out.println(objs[i]);
}
方式三:迭代器遍历,iterator()先获取到迭代器,然后通过迭代器中的方法
Collectionc = new ArrayList();
c.add("a");
c.add("b");
c.add("c");
//获取迭代器
Iteratorit = c.iterator();// 多态,事实上获取的是Iterator的实现类对象
while(it.hasNext()){
System.out.println(it.next());
}
方式四:列表迭代器遍历,listIterator()先获取到列表迭代器,然后通过列表迭代器的方法【只适用于List集合】
//创建集合对象
ArrayListlist = new ArrayList();
//添加元素
list.add("a");
list.add("b");
list.add("c");
ListIteratorlit = list.listIterator();
while(lit.hasNext()){
System.out.println(lit.next());
}
方式五:增强for(推荐)
Collection<String>c = new ArrayList<String>();
c.add("a");
c.add("b");
c.add("c");
for(Strings:c){
System.out.println(s);
}
9.常见几种集合的区别
Collection(单列集合)
|-List 有序,有索引,可以重复存取
|-ArrayList:底层是数组结构
查询快,增删慢。不安全,效率高。
|-LinkedList:底层是链表结构
查询慢,增删快。不安全,效率高。
|-Vector:数组结构
无论增删改查都慢。线程安全,效率低。
|-Set:无序(存取不一致),无索引,不能存储重复元素(能保证元素的唯一性)
|-HashSet 底层是哈希算法
|-LinkedHashSet:它是HashSet的子类。底层是链表实现,可以保证有序,无重复元素。(怎么存怎么取,去重)
|-TreeSet:可以排序
底层二叉树算法实现。
Map(双列集合)无序,无索引,不可以重复
|-HashMap:底层是哈希算法,针对键
允许null和值,线程不安全,效率高。键不能重复存取,值可以。
|-LinkedHashMap:底层哈希算法,和hashset一样键唯一,值可以改变。
|-Hashtable:
不允许null键和值,线程安全,效率低。
|-TreeMap:可以排序
底层是二叉树算法,针对键
9.1、Collection和Map的区别
Collection是单列集合的顶层接口,单列集合根据子体系不同,特点不同,List体系可以存储重复,Set体系不能存储重复元素。
Map是双列集合的顶层接口,Map集合是键不能重复。
9.2、ArrayList和Vector的区别?
Vector是JDK1.0出现的,比集合框架出现的还早
ArrayList是JDK1.2出现的
ArrayList查询快,增删慢,不安全,效率高。
Vector无论增删改查都慢,线程安全,效率低。
ArrayList的出现是为了替代Vector,因为ArrayList对Vector进行了两个改进:
(1)ArrayList是不同步的,效率高,Vector是同步的,效率低
(2)ArrayList中的方法使用更加方便,原来Vector提供的方法使用不方便,名称太长
ArrayList和Vector的主要区别?
1、Vector是线程安全的,源码中有很多的synchronized可以看出,而ArrayList不是。导致Vector效率无法和ArrayList相比;
2、ArrayList和Vector都采用线性连续存储空间,当存储空间不足的时候,ArrayList默认增加为原来的50%,Vector默认增加为原来的一倍;
3、Vector可以设置 capacityIncrement,而ArrayList不可以,从字面理解就是capacity容量,Increment增加,容量增长的参数。
9.3、HashMap和Hashtable的区别?
HashMap的出现是为了替代Hashtable
(1)HashMap是不同步的,效率高,Hashtable是同步的,效率低
(2)HashMap允许存储null键和null值,Hashtable不允许存储null键和null值
9.4、ArrayList和LinkedList的区别?分别用在什么场景?
1、ArrayList是最常用的List实现类,内部是通过数组实现的,它允许对元素进行快速随机访问。数组的缺点是每个元素之间不能有间隔,当数组大小不满足时需要增加 存储能力,就要讲已经有数组的数据复制到新的存储空间中。当从ArrayList的中间位置插入或者删除元素时,需要对数组进行复制、移动、代价比较高。因此,它适合随机查找和遍历,不适合插入和删除。
2、LinkedList是用链表结构存储数据的,很适合数据的动态插入和删除,随机访问和遍历速度比较慢。另外,他还提供了List接口中没有定义的方法,专门用于操作表头 和表尾元素,可以当作堆栈、队列和双向队列使用。
9.5、ArrayList默认大小是多少,是如何扩容的?
ArrayList默认构造的容量为10,没错。 因为ArrayList的底层是由一个Object[]数组构成的,而这个Object[]数组,默认的长度是10,所以有的文章会说 ArrayList长度容量为10。然而你所指的size()方法,指的是“逻辑”长度。ArrayList默认size()是0。ArrayList之后扩容会按照1.5倍增长。
9.6、List是线程安全的吗?如果要线程安全要怎么做?
不安全。实现线程安全方法:
- 1、使用synchronized关键字。
- 2、使用Collections.synchronizedList()。
9.7、怎么给List排序?
1、实体类自己实现比较。实现comparable接口:public interface Comparable ,里面就一个方法声明:public int compareTo(T o);用Java中提供的对集合进行操 作的工具类Collections,其中的sort方法
2、Collections提供的第二种排序方法sort(List list, Comparator c)。
9.8、List和Array之间如何互相转换?
list -> array =list.toArray()
array -> list = Arrays.asList()
迭代器循环法。
9.9、HashMap是线程安全的吗?线程安全的Map都有哪些?性能最好的是哪个?
HashMap不安全,线程安全的Map有hashTable、ConcurrentHashMap、通过Collections.synchronizedMap()。性能最好的是ConcurrentHashMap。通过分段锁来 实现线程安全,默认并发数是16,扩容是按16的倍数增长。hashMAP实现原理是由hash数组为主,存储一个key-val结构entry类的数组,当出现hash冲突时,通过链表来 解决问题。Jdk1.8中引入了红黑树,当链表的长度大于8 时会自动将链表转成红黑树。
hashMAp线程不安全,hashtable线程安全。Hash Table时不支持null的,hashmap支持nulll是因为将null放入hash表的0个buchect。
实现线程安全的两种方式:Collecttions.synchronizedMap()也可以用ConcurrentHashMap。Jdk7时通过桶的分段锁的方式实现,默认设置16段,当并发度超过16段 时按2的指幂增长。JDK8放弃分段锁的实现,通过节点的原子锁来实现锁。将分段锁的悲观锁改成平台型的乐观锁。乐观锁无法保证脏读,所以要通过原子快照来保证。
[concurrenthashmap 线程安全原理] (http://www.importnew.com/22007.htm
9.10、使用HashMap有什么性能问题吗?
当大量的key出现hash冲突时,链表会变长,从而影响get、put效率。而且当链表长度达到负载因子设置0.75长度值时,还会有扩容操作会很影响性能。
9.11、hashMap默认大小是多少?内部是怎么扩容的?
hashMap这里的默认值是16。当元素个数大于0.75×16 = 12时,HashMap会自动进行扩容。 oldlength *2个
10、String、StringBuffer、StringBuilder有什么区别?
1、首先说运行速度,或者说是执行速度,在这方面运行速度快慢为:StringBuilder > StringBuffer > String
2、在线程安全上,StringBuilder是线程不安全的,而StringBuffer是线程安全的
3、应用场景:
String-适用于少量的字符串操作的情况;
StringBuilder-适用于单线程下在字符缓冲区进行大量操作的情况;StringBuilder的出现是为了替代StringBuffer;
StringBuffer-StringBuffer中很多方法可以带有synchronized关键字,可以保证线程是安全的,效率低。适用多线程下在字符缓冲区 进行大量操作的情况。
11、多线程的实现方式
方式一:继承Thread类
继承有单一继承的局限性,如果一个类已经继承了其他类,就再也不能继承Thread类了,或者如果继承了Thread类,以后要继承其他类也不可以了。
继承Thread类就是Thread类的子类,子类可以直接使用父类Thread中的方法,更方便
(1)自定义一个类继承Thread类
class MyThreadextends Thread {
// (2)重写run()方法,它是多线程执行的核心代码
@override
public voidrun() {
}
}
public class Test {
publicstatic void main(String[] args){
//(3)测试类中创建自定义类的对象
MyThreadmt = new MyThread();
//(4)调用start()方法开启线程
mt.start();
}
}
方式二:实现Runnable接口
没有单一继承的局限性,即使自定义类已经继承了其他类,也是可以使用这种方式的
没有继承Thread类,不能直接使用Thread类中的方法,如果要使用首先必须通过currentThread()这个方法先获取到线程对象,然后在调用方法
//(1)自定义一个类实现Runnable接口
classMyThread2 implements Runnable {
//(2)重写run()方法
publicvoid run(){
}
}
publicclass Test2 {
publicstatic void main(String[] args) {
//(3)在测试类中创建自定义类的对象
MyThread2mt2 = new MyThread2();
//(4)创建Thread类的对象,将自定义类的对象当做参数传递到构造方法中
Threadth = new Thread(mt2);
//(5)使用Thread类的对象调用start()启动线程
th.start();
}
}
方式三:创建有返回值的线程
第一步:实现callable接口,复写call方法,得到一个返回值
第二步:创建FutureTask类,将callable对象传入
第三步:使用Thread,运行FutureTask
第四步:等待10秒
第五步:通过FutureTask.get()获取返回值
12、启动线程调用的start()方法还是run()方法?
调用start()启动线程。
start()在底层会自动调用run()
如果线程对象直接调用run()方法,就相当于调用普通的run()方法,不会启动多线程
13、线程和进程
13.1、区别?
进程:正在运行的程序,占用了内存资源和CPU的程序,也就是说进程是可以独立运行的一段程序。进程包含线程。
线程:进程中的一条执行路径。是CPU调度和分派的基本单位。线程自己基本不拥有系统资源。在运行时,只是暂时用一些计数器、寄存器和栈。
一个线程只能属于一个进程,而一个进程可以有多个线程,但至少有一个线程(通常说的主线程)
从三个角度来剖析二者之间的区别:
1、调度:线程作为调度和分配的基本单位,进程作为拥有资源的基本单位。
2、并发性:不仅进程之间可以并发执行,同一个进程的多个线程之间也可以并发执行。
3、拥有资源:进程是拥有资源的一个独立单位,线程不拥有系统资源,但可以访问隶属于进程的资源。
13.2、一个线程的生命周期有哪几种状态?它们之间如何流转的?
1、新建状态(New):新创建了一个线程对象。
2、就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的start()方法。该状态的线程位于“可运行线程池”中,变得可运行,只等待获取CPU的使用权。 即在就绪状态的进程除CPU之外,其它的运行所需资源都已全部获得。
3、运行状态(Running):就绪状态的线程获取了CPU,执行程序代码。4、阻塞状态(Blocked):阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线 程进入就绪状态,才有机会转到运行状态。
阻塞的情况分三种:
- 1)、等待阻塞:运行的线程执行wait()方法,该线程会释放占用的所有资源,JVM会把该线程放入“等待池”中。进入这个状态后,是不能自动唤醒的,必须依靠其他 线程调用notify()或notifyAll()方法才能被唤醒,
- 2)、同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入“锁池”中。
- 3)、其他阻塞:运行的线程执行sleep()或join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超 时、或者I/O处理完毕时,线程重新转入就绪状态。
死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。
13.3、多线程同步有哪几种方法?
1、同步方法,即有synchronized关键字修饰的方法。
2.同步代码块,即有synchronized关键字修饰的语句块。
3、使用特殊域变量(volatile)实现线程同步:
- a).volatile关键字为域变量的访问提供了一种免锁机制,
- b).使用volatile修饰域相当于告诉虚拟机该域可能会被其他线程更新,
- c).因此每次使用该域就要重新计算,而不是使用寄存器中的值d.volatile不会提供任何原子操作,它也不能用来修饰final类型的变量
4、使用重入锁实现线程同步,在JavaSE5.0中新增了一个java.util.concurrent包来支持同步。
5、使用局部变量实现线程同步 ,如果使用ThreadLocal管理变量,则每一个使用该变量的线程都获得该变量的副本,副本之间相互独立,这样每一个线程都可以随意 修改自己的变量副本,而不会对其他线程产生影响。
13.4、多线程之间如何进行通信?
1、同步,这里讲的同步是指多个线程通过synchronized关键字这种方式来实现线程间的通信。
2、while轮询的方式。
3、wait/notify机制。
4、管道通信就是使用java.io.PipedInputStream 和java.io.PipedOutputStream进行通信
13.5、新建T1、T2、T3三个线程,如何保证它们按顺序执行?
T3先执行,在T3的run中,调用t2.join,让t2执行完成后再执行t3 在T2的run中,调用t1.join,让t1执行完成后再让T2执行
13.6、为什么要使用线程池?
线程的执行过程:
创建(t1) 运行(t2) 销毁(t3) 线程运行的总时间 T= t1+t2+t3; 假如,有些业务逻辑需要频繁的使用线程执行某些简单的任务,那么很多时间都会浪费t1和t3上。为了避免这种问题,JAVA提供了线程池 在线程池中的线程可以复 用,当线程运行完任务之后,不被销毁.
13.7、常用的几种线程池并讲讲其中的工作原理。
1、newCachedThreadPool,创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
线程池特点:
工作线程的创建数量几乎没有限制(其实也有限制的,数目为Interger. MAX_VALUE), 这样可灵活的往线程池中添加线程。
如果长时间没有往线程池中提交任务,即如果工作线程空闲了指定的时间(默认为1分钟),则该工作线程将自动终止。终止后,如果你又提交了新的任务,则线程池 重新创建一个工作线程。在使用CachedThreadPool时,一定要注意控制任务的数量,否则,由于大量线程同时运行,很有会造成系统瘫痪。
2、newFixedThreadPool,定长线程。创建一个指定工作线程数量的线程池。每当提交一个任务就创建一个工作线程,如果工作线程数量达到线程池初始的最大数,则 将提交的任务存入到池队列中。FixedThreadPool是一个典型且优秀的线程池,它具有线程池提高程序效率和节省创建线程时所耗的开销的优点。但是,在线程池 空闲时,即线程池中没有可运行任务时,它不会释放工作线程,还会占用一定的系统资源。
3、newScheduledThreadPool,定长、周期性线程池
4、newSingleThreadExecutor,单线程。创建一个单线程化的Executor,即只创建唯一的工作者线程来执行任务,它只会用唯一的工作线程来执行任务,保证所有任 务按照指定顺序(FIFO, LIFO, 优先级)执行。如果这个线程异常结束,会有另一个取代它,保证顺序执行。单工作线程最大的特点是可保证顺序地执行各个任务, 并且在任意给定的时间不会有多个线程是活动的。
13.8、什么是守护线程?有什么用?
1、守护线程就是main同生共死,当main退出,它将终止,而普通线程是在任务执行结束才停止。
2.Java虚拟机在它所有非守护线程已经离开后自动离开。守护线程则 是用来服务用户线程的,如果没有其他用户线程在运行,那么就没有可服务对象,也就没有理由继续下去。守护线程是用来保证用户线程,有效可靠的执行。
13.9、Java中notify和notifyAll有什么区别?
notify是通知一个线程获取锁,notifyAll是通知所有相关的线程去竞争锁。notify不能保证获得锁的线程,真正需要锁,并且可能产生死锁。
13.10、提交任务时线程池队列已满会时发会生什么?
如果一个任务不能被调度执行那么ThreadPoolExecutor’s submit()方法将会抛出一个RejectedExecutionException异常。
13.11、ThreadLocal实现原理是什么?
ThreadLocal整体上感觉是,一个包装类。声明了这个类的对象之后,每个线程的数据其实还是在自己线程内部通过threadLocals引用到的自己的数据。只是通过 ThreadLocal访问这个数据而已。当线程中的threadlocalmap是null的时候,会调用createmap创建一个map。同时根据函数参数设置上初始值。也就是说,当前 线程的threadlocalmap是在第一次调用set的时候创建map并且设置上相应的值的。
14、UDP和TCP区别
14.1、UDP:面向无连接,速度快,不安全,区分发送端和接收端
UDP是无连接通信协议,即在数据传输时,数据的发送端和接收端不建立逻辑连接。
简单来说,当一台计算机向另外一台计算机发送数据时,发送端不会确认接收端是否存在,就会发出数据,同样接收端在收到数据时,也不会向发送端反馈是否收到数据。
由于使用UDP协议消耗资源小,通信效率高,所以通常都会用于音频、视频和普通数据的传输例如视频会议都使用UDP协议,因为这种情况即使偶尔丢失一两个数据包,也不会对接收结果产生太大影响。
14.2、TCP:面向连接,速度稍慢,安全,区分客户端和服务端
TCP通信同UDP通信一样,都能实现两台计算机之间的通信,通信的两端都需要创建socket对象。区别在于,UDP中只有发送端和接收端,不区分客户端与服务器端,计算机之间可以任意地发送数据。
而TCP通信是严格区分客户端与服务器端的,在通信时,必须先由客户端去连接服务器端才能实现通信,服务器端不可以主动连接客户端,并且服务器端程序需要事先启动,等待客户端的连接。
14.3、TCP为什么是三次握手,不是两次?
TCP与UDP基本区别:
- 1、基于连接与无连接
- 2、TCP要求系统资源较多,UDP较少;
- 3、UDP程序结构较简单
- 4、流模式(TCP)与数据报模式(UDP);
- 5、TCP保证数据正确性,UDP可能丢包
- 6、TCP保证数据顺序,UDP不保证
三次握手过程:
第一次握手:建立连接时,客户端发送syn包(syn=j)到服务器,并进入SYNSEND ,等待服务器确认。
第二次握手:服务器收到syn包,必须确认客户端的SYN ( ack=j+1 ),同时自己也发送一个SYN包(syn=k)即SYN+ACK包 ,此时服务器进入 SYNRECV状态;
第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手。
15.什么是反射?
在运行时期,可以获取和调用类的所有成员,包括私有。
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法。
对于任意一个对象,都能够调用它的任意一个方法,这种动态获取的以及动态调用对象的方法的功能称为java语言的反射机制。
字节码文件获取的三种方式:
1. 对象名.getCalss(); // 方法来自于Object 对象已经存在的情况下, 可以使用这种方式
2. 类名.class //类名.class这是一个静态的属性, 只要知道类名, 就可以获取
3.Class.forName(“com.test.Student”); // 通过Class类中的静态方法, 指定字符串, 该字符串是类的全类名(包名+类名)// 此处将会抛出异常都系 ClassNotFoundException 防止传入错误的类名
16. 什么是死锁 (deadlock) ?
两个线程或两个以上线程都在等待对方执行完毕才能继续往下执行的时候就发生了死锁。结果就是这些线程都陷入了无限的等待中。
产生死锁的原因主要是:
- (1) 因为系统资源不足。
- (2) 进程运行推进的顺序不合适。
- (3) 资源分配不当等。
产生死锁的四个必要条件:
- (1) 互斥条件:一个资源每次只能被一个进程使用。
- (2) 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
- (3) 不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。
- (4) 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
解决死锁:
使用多线程的时候,一种非常简单的避免死锁的方式就是:指定获取锁的顺序,并强制线程按照指定的顺序获取锁。因此,如果所有的线程都是以同样的顺序加锁和释放锁,就不会出现死锁了。
17. throw与throws的比较
- 1、throws出现在方法函数头;而throw出现在函数体。
- 2、throws表示出现异常的一种可能性,并不一定会发生这些异常;throw则是抛出了异常,执行throw则一定抛出了某种异常对象。
- 3、两者都是消极处理异常的方式(这里的消极并不是说这种方式不好),只是抛出或者可能抛出异常,但是不会由函数去处理异常,真正的处理异常由函数的上层调用处理。
18、Http1与Http2的区别?
- 1、HTTP2使用的是二进制传送,HTTP1.X是文本(字符串)传送。 大家都知道HTTP1.X使用的是明文的文本传送,而HTTP2使用的是二进制传送,二进制传送的单位是帧和 流。帧组成了流,同时流还有流ID标示,通过流ID就牵扯出了第二个区别。
- 2、HTTP2支持多路复用。因为有流ID,所以通过同一个http请求实现多个http请求传输变成了可能,可以通过流ID来标示究竟是哪个流从而定位到是哪个http请求。
- 3、HTTP2头部压缩。HTTP2通过gzip和compress压缩头部然后再发送,同时客户端和服务器端同时维护一张头信息表,所有字段都记录在这张表中,这样后面每次传输只 需要传输表里面的索引Id就行,通过索引ID就可以知道表头的值了。
- 4、HTTP2支持服务器推送。HTTP2支持在客户端未经请求许可的情况下,主动向客户端推送内容
19、JDK引入泛型是解决什么问题的?
Java语言引入泛型的好处是安全简单。 在Java SE 1.5之前,没有泛型的情况的下,通过对类型Object的引用来实现参数的“任意化”,“任意化”带来的缺点是要做显式的强制类型转换,而这种转换是要求开发者对实际参数类型可以预知的情况下进行的。对于强制类型转换错误的情况,编译器可能不提示错误,在运行的时候才出现异常,这是一个安全隐患。 泛型的好处是在编译的时候检查类型安全,并且所有的强制转换都是自动和隐式的,提高代码的重用率。
20、谈谈hashCode与equals之间的关系?
因为如果只覆盖了equals而没有覆盖hashCode, 则两个不同的instance a和b虽然equals结果(业务逻辑上)相等,但却会有不同的hashcode,这样hashmap里面会同时存在a和b,而实际上我们需要hashmap里面只能保存其中一个,因为从业务逻辑方向看它们是相等的。
equals方法和hashCode方法如果不同时按你自己逻辑覆盖的话,HashMap就会出问题。比如你只覆盖了equals方法而没有覆盖hashCode方法,那么HashMap在第一步寻找链表的时候会出错,有同样值的两个对象Key1和Key2并不会指向同一个链表或桶,因为你没有提供自己的hashCode方法,那么就会使用Object的hashCode方法,该方法是根据内存地址来比较两个对象是否一致,由于Key1和Key2有不桶的内存地址,所以会指向不同的链表,这样HashMap会认为key2不存在,虽然我们期望Key1和Key2是同一个对象;反之如果只覆盖了hashCode方法而没有覆盖equals方法,那么虽然第一步操作会使Key1和Key2找到同一个链表,但是由于equals没有覆盖,那么在遍历链表的元素时,key1.equals(key2)也会失败(事实上Object的equals方法也是比较内存地址),从而HashMap认为不存在Key2对象,这同样也是不正确的。
21、谈谈反射机制?
1.何谓反射机制。
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
2.反射机制如何实现。
Java程序在运行时,Java运行时系统一直对所有的对象进行所谓的运行时类型标识。这项信息纪录了每个对象所属的类。虚拟机通常使用运行时类型信息选准正确方法去执行,用来保存这些类型信息的类是Class类。也就是说,ClassLoader找到了需要调用的类时(java为了调控内存的调用消耗,类的加载都在需要时再进行,很抠但是很有效),就会加载它,然后根据.class文件内记载的类信息来产生一个与该类相联系的独一无二的Class对象。该Class对象记载了该类的字段,方法等等信息。以后jvm要产生该类的实例,就是根据内存中存在的该Class类所记载的信息(Class对象应该和我所了解的其他类一样会在堆内存内产生、消亡)来进行。而java中的Class类对象是可以人工自然性的(也就是说开放的)得到的(虽然你无法像其他类一样运用构造器来得到它的实例,因为Class对象都是jvm产生的。不过话说回来,客户产生的话也是无意义的),而且,更伟大的是,基于这个基础,java实现了反射。
3.获取Class对象有三种方式:
- 1).通过Object类的getClass()方法。
- 2).通过Class类的静态方法——forName()来实现。
- 3).如果T是一个已定义的类型的话,在java中,它的.class文件名:T.class就代表了与其匹配的Class对象。
22、常用的JVM设置参数都有哪些?
整体考虑堆大小
-Xms3550m, 初始化堆大小。通常情况和-Xmx大小设置一样,避免虚拟机频繁自动计算后调整堆大小。-Xmx3550m,最大堆大小。
新生代
-xmn2g,新生代大小。Sun官方推荐配置为整个堆的3/8。-XX:SurvivorRatio=8。Eden和Survivor的比值。
老年代
老年代=整个堆大小-新生代-永久代
永久代
-XX:Permsize=512m,设置永久代初始值。-XX:MaxPermsize=512m,设置永久代的最大值。 注:Java8没有永久代说法,它们被称为元空间,-XX:MetaspaceSize=N
考虑本机直接内存
-XX:MaxDirectMemorySize=100M。默认与Java堆大最大值(-Xmx)
考虑虚拟机栈
每个线程池的堆栈大小。在jdk5以上的版本,每个线程堆栈大小为1m,jdk5以前的版本是每个线程池大小为256k。一般设置256k。 -Xss256K。
......
23、你怎么理解cookie和session,有哪些不同点?
1、cookie数据存放在客户的浏览器上,session数据放在服务器上。
2、cookie不是很安全,别人可以分析存放在本地的cookie并进行cookie欺骗,考虑到安全应当使用session。
3、session会在一定时间内保存在服务器上。当访问增多,会比较占用你服务器的性能,考虑到减轻服务器性能方面,应当使用cookie。
4、单个cookie保存的数据不能超过4K,很多浏览器都限制一个站点最多保存20个cookie。
5、可以考虑将登陆信息等重要信息存放为session,其他信息如果需要保留,可以放在cookie中
24、什么是web缓存?有什么优点?
Web缓存游走于服务器和客户端之间。这个服务器可能是源服务器(资源所驻留的服务器Add),数量可能是1个或多个;这个客户端也可能是1个或多个。Web缓存就在服 务器-客户端之间搞监控,监控请求,并且把请求输出的内容(例如html页面、 图片和文件)(统称为副本)另存一份;然后,如果下一个请求是相同的URL,则直接请求 保存的副本,而不是再次麻烦源服务器。
使用缓存的2个主要原因:
降低延迟:缓存离客户端更近,因此,从缓存请求内容比从源服务器所用时间更少,呈现速度更快,网站就显得更灵敏。
降低网络传输:副本被重复使用,大大降低了用户的带宽使用,其实也是一种变相的省钱(如果流量要付费的话),同时保证了带宽请求在一个低水平上,更容易维护 了。
25、简述Servlet与JSP的关系?
最重要的一点就是JSP就是servlet,jsp继承了servlet。
26、session共享有哪些方案?
1、rediss实现共享
2、服务器插件复制
3、文件服务器共享
4、cookie+鉴权中心
5、cookie加密存储
27、什么是过滤器、拦截器、监听器,它们的顺序是怎样的?
监听器> 过滤器 > 拦截器
28、Servlet3.0有哪些新规范?
1、注解支持;Servlet、Filter、Listener无需在web.xml中进行配置,可以通过对应注解进行配置;
2.支持Web模块;
3.Servlet异步处理;
4.文件上传API简化
29、常见的攻击手段有哪些?如何防范?
1、SQL注入攻击。通过预编译PrepareStatement,规范sql习惯尽量不要用in等。in 操作,使用了预编译,还是仍然还是通过字符串拼接。 Web端有效性检验。限制 字符串输入的长度,服务端不用拼接SQL字符串。
2、XSS跨站点脚本攻击。前端服务端,同时需要字符串输入的长度限制。前端服务端,同时需要对HTML转义处理。将其中的”<”,”>”等特殊字符进行转义编码。
3、CSRF攻击。token机制。在HTTP请求中进行token验证,如果请求中没有token或者token内容不正确,则认为CSRF攻击而拒绝该请求。 验证码。通常情况下,验证 码能够很好的遏制CSRF攻击,但是很多情况下,出于用户体验考虑,验证码只能作为一种辅助手段,而不是最主要的解决方案。 referer识别。在HTTP Header中 有一个字段Referer,它记录了HTTP请求的来源地址。如果Referer是其他网站,就有可能是CSRF攻击,则拒绝该请求。
4、文件上传漏洞。 对上传的文件类型进行白名单校验,只允许上传可靠类型。 上传的文件需要进行重新命名,使攻击者无法猜想上传文件的访问路径,将极大地增加 攻击成本,同时向shell.php.rar.ara这种文件,因为重命名而无法成功实施攻击。 限制上传文件的大小。 单独设置文件服务器的域名。访问控制,一般来 说,“基于URL的访问控制”是最常见的。
30、RESTFUL API 有哪些设计原则?
1、协议。API与用户的通信协议,总是使用HTTPs协议。2.域名。应该尽量将API部署在专用域名之下。如果确定API很简单,不会有进一步扩展,可以考虑放在主域名 下。
3、版本(Versioning。应该将API的版本号放入URL。
4.路径。路径又称"终点"(endpoint),表示API的具体网址。在RESTful架构中,每个网址代表一种资源(resource),所以网址中不能有动词,只能有名词,而且所 用的名词往往与数据库的表格名对应。一般来说,数据库中的表都是同种记录的"集合"(collection),所以API中的名词也应该使用复数。
5.HTTP动词。对于资源的具体操作类型,由HTTP动词表示。
6.过滤信息。如果记录数量很多,服务器不可能都将它们返回给用户。API应该提供参数,过滤返回结果。
7.状态码。服务器向用户返回的状态码和提示信息。
8.错误处理。如果状态码是4xx,就应该向用户返回出错信息。一般来说,返回的信息中将error作为键名,出错信息作为键值即可。
9.返回结果要预期类型。
10.Hypermedia API。ESTful API最好做到Hypermedia,即返回结果中提供链接,连向其他API方法,使得用户不查文档,也知道下一步应该做什么。
11.服务器返回的数据格式,应该尽量使用JSON,避免使用XML。