javaSE八股文
1.多线程部分
创建线程有几种不同的方式?你喜欢哪一种?为什么?
有三种方式可以用来创建线程:
-
继承Thread类
-
实现Runnable
-
实现Callable接口、使用Executor框架来创建线程池(实现Callable接口-创建ExecutorService 实例-service.submit(接口实现类)-service.shutdown) 实现Runnable接口这种方式更受欢迎,因为这不需要继承Thread类。在应用设计中已经继承了别的对象的情况下,这需要多继承(而Java不支持多继承),只能实现接口。同时,线程池也是非常高效的,很容易实现和使用。
Thread中的run()和start()方法有何区别?
t.start()会导致run()方法被调用,run()方法中的内容称为线程体,它就是这个线程需要执行的工作。 用start()来启动线程,实现了真正意义上的启动线程,此时会出现异步执行的效果,即在线程的创建和启动中所述的随机性。 而如果使用run()来启动线程,就不是异步执行了,而是同步执行,不会达到使用线程的意义
sleep和wait的区别:
1、sleep是Thread的静态方法,wait是Object的方法,任何对象实例都能调用。
2、sleep不会释放锁,它也不需要占用锁。wait会释放锁,但调用它的前提是当前线程占有锁(即代码要在synchronized中)。
3、它们都可以被interrupted方法中断。
锁的分类
可重入锁:Synchronized和ReentrantLook都是可重入锁,锁的可重入性标明了锁是针对线程分配方式而不是针对方法。例如调用Synchronized方法A中可以调用Synchronized方法B,而不需要重新申请锁。
读写锁:按照数据库事务隔离特性的类比读写锁,在访问统一个资源(一个文件)的时候,使用读锁来保证多线程可以同步读取资源。ReadWriteLock是一个读写锁,通过readLock()获取读锁,通过writeLock()获取写锁。
可中断锁:可中断是指锁是可以被中断的,Synchronized内置锁是不可中断锁,ReentrantLock可以通过lockInterruptibly方法中断显性锁。例如线程B在等待等待线程A释放锁,但是线程B由于等待时间太久,可以主动中断等待锁。
公平锁:公平锁是指尽量以线程的等待时间先后顺序获取锁,等待时间最久的线程优先获取锁。synchronized隐性锁是非公平锁,它无法保证等待的线程获取锁的顺序,ReentrantLook可以自己控制是否公平锁。
Synchronized和Lock比较
实现:
-
Synchronized:底层使用指令码方式来控制锁的,映射成字节码指令就是增加来两个指令:monitorenter和monitorexit。当线程执行遇到monitorenter指令时会尝试获取内置锁,如果获取锁则锁计数器+1,如果没有获取锁则阻塞;当遇到monitorexit指令时锁计数器-1,如果计数器为0则释放锁。
-
Lock:底层是CAS乐观锁,依赖AbstractQueuedSynchronizer类,把所有的请求线程构成一个CLH队列。而对该队列的操作均通过Lock-Free(CAS)操作。
-
Synchronized是关键字,内置语言实现,Lock是接口。
-
Synchronized在线程发生异常时会自动释放锁,因此不会发生异常死锁。Lock异常时不会自动释放锁,所以需要在finally中实现释放锁。
-
Lock是可以中断锁,Synchronized是非中断锁,必须等待线程执行完成释放锁。
-
Lock可以使用读锁提高多线程读效率。
同步方法和同步代码块的区别是什么?
在Java语言中,每一个对象有一把锁。线程可以使用synchronized关键字来获取对象上的锁。synchronized关键字可应用在方法级别(粗粒度锁:这里的锁对象可以是This)或者是代码块级别(细粒度锁:这里的锁对象就是任意对象)
-
同步方法默认用this或者当前类class对象作为锁;
-
同步代码块可以选择以什么来加锁,比同步方法要更细颗粒度,我们可以选择只同步会发生同步问题的部分代码而不是整个方法;
-
同步方法使用关键字 synchronized修饰方法,而同步代码块主要是修饰需要进行同步的代码,用 synchronized(object){代码内容}进行修饰。
如何确保N个线程可以访问N个资源同时又不导致死锁?
使用多线程的时候,一种非常简单的避免死锁的方式就是:指定获取锁的顺序,并强制线程按照指定的顺序获取锁。因此,如果所有的线程都是以同样的顺序加锁和释放锁,就不会出现死锁了。
线程状态?
创建、就绪、阻塞、运行、死亡
2.类、方法、注解相关
Overload和Override的区别。Overloaded的方法是否可以改变返回值的类型?
方法的重写Overriding和重载Overloading是Java多态性的不同表现。重写Overriding是父类与子类之间多态性的一种表现,重载O verloading是一个类中多态性的一种表现。如果在子类中定义某方法与其父类有相同的名称和参数,我们说该方法被重写(Overriding)。子类的对象使用这个方法时,将调用子类中的定义,对它而言,父类中的定义如同被”屏蔽”了。如果在一个类中定义了多个同名的方法,它们或有不同的参数个数或有不同的参数类型,则称为方法的重载(Overloading)。Overloaded的方法是可以改变返回值的类型。
接口和抽象类的区别是什么?
Java提供和支持创建抽象类和接口。它们的实现有共同点,不同点在于: • 接口中所有的方法隐含的都是抽象的。而抽象类则可以同时包含抽象和非抽象的方法。 • 类可以实现很多个接口,但是只能继承一个抽象类 • 类如果要实现一个接口,它必须要实现接口声明的所有方法。但是,类可以不实现抽象类声明的所有方法,当然,在这种情况下,类也必须得声明成是抽象的。 • 抽象类可以在不提供接口方法实现的情况下实现接口。 • Java接口中声明的变量默认都是final的。抽象类可以包含非final的变量。 • Java接口中的成员函数默认是public的。抽象类的成员函数可以是private,protected或者是public。 • 接口是绝对抽象的,不可以被实例化。抽象类也不可以被实例化,但是,如果它包含main方法的话是可以被调用的。
JAVA反射获取类的三种方式
-
调用对象的.getClass()方法
-
调用某个类的.class属性
-
使用Class.forName+类的全路径
JAVA的四种标准元注解
@Target 修饰对象范围,例如types, packages @Retention 定义保留的时间长短,例如SOURCE,RUNTIME @Document描述javadoc @Inherited阐述某个被标注的类型是被继承的。作用:如果一个类用上了@Inherited修饰的注解,那么其子类也会继承这个注解。但是如果接口用上个@Inherited修饰的注解,其实现类不会继承这个注解;或者父类的方法用了@Inherited修饰的注解,子类也不会继承这个注解。这样操作后,子类可以获取父类的注解,字段等信息。
什么时候需要复写hashcode()和compartTo方法
HashMap中实现了一个Entry[]数组,数组的每个item是一个单向链表的结构,当我们put(key, value)的时候,HashMap首先会newItem.key.hashCode()作为该newItem在Entry[]中存储的下标,要是对应的下标的位置上没有任何item,则直接存储上去,要是已经有oldItem存储在了上面,那就会判断oldItem.key.equals(newItem.key),那么要是我们把上面的Person作为key进行存储的时候,重写了equals()方法,但没重写hashCode()方法,当我们去put()的时候,首先会通过hashCode() 计算下标,由于没有重写hashCode(),那么实质是完整的Object的hashCode(),会受到Object多个属性的影响,本来equals的两个Person对象,反而得到了两个不同的下标。 同样的,HashMap在get(key)的过程中,也是首先调用hashCode()计算item的下标,然后在对应下标的地方找,要是为null,就返回null,要是 != null,会去调用equals()方法,比较key是否一致,只有当key一致的时候,才会返回value,要是我们没有重写hashCode()方法,本来有的item,反而会找不到,返回null结果。 所以,要是你重写了equals()方法,而你的对象可能会放入到散列(HashMap,HashTable或HashSet等)中,那么还必须重写hashCode(), 如果你的对象有可能放到有序队列(实现了Comparable)中,那么还需要重写compareTo()的方法。
3.容器
HashMap和Hashtable有什么区别?
• HashMap和Hashtable都实现了Map接口,因此很多特性非常相似。但是,他们有以下不同点: • HashMap允许键和值是null,而Hashtable不允许键或者值是null。 • Hashtable是同步的,而HashMap不是。因此,HashMap更适合于单线程环境,而Hashtable适合于多线程环境。 • HashMap提供了可供应用迭代的键的集合,因此,HashMap是快速失败的。另一方面,Hashtable提供了对键的列举(Enumeration)。 o 一般认为Hashtable是一个遗留的类。
数组(Array)和列表(ArrayList)有什么区别?什么时候应该使用Array而不是ArrayList?
• Array可以包含基本类型和对象类型,ArrayList只能包含对象类型。 • Array大小是固定的,ArrayList的大小是动态变化的。 • ArrayList提供了更多的方法和特性,比如:addAll(),removeAll(),iterator()等等。 • 对于基本类型数据,集合使用自动装箱来减少编码工作量。但是,当处理固定大小的基本数据类型的时候,这种方式相对比较慢。
ArrayList和LinkedList有什么区别?
ArrayList和LinkedList都实现了List接口,他们有以下的不同点: • ArrayList是基于索引的数据接口,它的底层是数组。它可以以O(1)时间复杂度对元素进行随机访问。与此对应,LinkedList是以元素链表的形式存储它的数据,每一个元素都和它的前一个和后一个元素链接在一起,在这种情况下,查找某个元素的时间复杂度是O(n)。 • 相对于ArrayList,LinkedList的插入,添加,删除操作速度更快,因为当元素被添加到集合任意位置的时候,不需要像数组那样重新计算大小或者是更新索引。 • LinkedList比ArrayList更占内存,因为LinkedList为每一个节点存储了两个引用,一个指向前一个元素,一个指向下一个元素。
HashSet和TreeSet有什么区别?
HashSet是由一个hash表来实现的,因此,它的元素是无序的。add(),remove(),contains()方法的时间复杂度是O(1)。 另一方面,TreeSet是由一个树形的结构来实现的,它里面的元素是有序的。因此,add(),remove(),contains()方法的时间复杂度是O(logn)。
说出ArrayList,Vector, LinkedList的存储性能和特性
List的子类特点 ArrayList: 底层数据结构是数组,查询快,增删慢 线程不安全,效率高 Vector: 底层数据结构是数组,查询快,增删慢 线程安全,效率低 LinkedList: 底层数据结构是链表,查询慢,增删快 线程不安全,效率高
String,StringBuffer,StringBuilder
String底层是final char value[]。因此其不可变。 StringBuffer和StringBuilder都是继承自AbstractStringBuilder,其底层使用的是char value[](无final),所以可变。StringBuffer是线程安全的(使用synchronized保证),StringBuilder不是。 每次对String类型进行改变时,会生成一个新的String对象,然后把指针指向新的String,但是其他两个都是操作自身。
4.javaWeb
RPC和http的区别
RPC:Remote Produce Call远程过程调用,类似的还有RMI(Remote Methods Invoke 远程方法调用,是JAVA中的概念,是JAVA十三大技术之一)。自定义数据格式,基于原生TCP通信,速度快,效率高。早期的webservice,现在热门的dubbo,都是RPC的典型
RPC的框架:webservie(cxf)、dubbo RMI的框架:hessian Http:http其实是一种网络传输协议,基于TCP,规定了数据传输的格式。现在客户端浏览器与服务端通信基本都是采用Http协议。也可以用来进行远程服务调用。缺点是消息封装臃肿。
现在热门的Rest风格,就可以通过http协议来实现。
http的实现技术:HttpClient 相同点:底层通讯都是基于socket,都可以实现远程调用,都可以实现服务调用服务
不同点: RPC:框架有:dubbo、cxf、(RMI远程方法调用)Hessian 当使用RPC框架实现服务间调用的时候,要求服务提供方和服务消费方 都必须使用统一的RPC框架,要么都dubbo,要么都cxf
跨操作系统在同一编程语言内使用 优势:调用快、处理快
http:框架有:httpClient 当使用http进行服务间调用的时候,无需关注服务提供方使用的编程语言,也无需关注服务消费方使用的编程语言,服务提供方只需要提供restful风格的接口,服务消费方,按照restful的原则,请求服务,即可
跨系统跨编程语言的远程调用框架 优势:通用性强
如何选择? 既然两种方式都可以实现远程调用,我们该如何选择呢?
速度来看,RPC要比http更快,虽然底层都是TCP,但是http协议的信息往往比较臃肿 难度来看,RPC实现较为复杂,http相对比较简单 灵活性来看,http更胜一筹,因为它不关心实现细节,跨平台、跨语言。 因此,两者都有不同的使用场景:
如果对效率要求更高,并且开发过程使用统一的技术栈,那么用RPC还是不错的。 如果需要更加灵活,跨语言、跨平台,显然http更合适 那么我们该怎么选择呢?
微服务,更加强调的是独立、自治、灵活。而RPC方式的限制较多,因此微服务框架中,一般都会采用基于Http的Rest风格服务。 (注:原文链接:HTTP与RPC的区别_谦虚使人发胖的博客-CSDN博客_http和rpc
cookie与session区别对比:
-
cookie数据存放在客户的浏览器上,session数据放在服务器上
-
cookie不是很安全,别人可以分析存放在本地的COOKIE并进行COOKIE欺骗,如果主要考虑到安全应当使用session
-
session会在一定时间内保存在服务器上。当访问增多,会比较占用你服务器的性能,如果主要考虑到减轻服务器性能方面,应当使用COOKIE
-
单个cookie在客户端的限制是3K,就是说一个站点在客户端存放的COOKIE不能3K。
所以:将登陆信息等重要信息存放为SESSION;其他信息如果需要保留,可以放在COOKIE中
JDBC过程
//加载数据库驱动(mysql其实不做这一步也可以,类会自动去注册驱动,但Oracle是必须要做的) Class.forName("com.mysql.jdbc.Driver"); //通过驱动管理类获取数据库连接 Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/mybatis? characterEncoding=utf-8", "root", "root"); //获取预处理对象statement PreparedStatement preparedStatement = connection.prepareStatement(sql); //执行SQL ResultSet resultSet = preparedStatement.executeQuery();