1、第一个问题:List list1=new ArrayList();与ArrayList list2=new ArrayList();他们两个之间的区别?
区别在于,前者list1--有些ArrayList类具有的,但是List接口没有的属性和方法,它就不能再用了,后者创建一对象则保留了ArrayList的所有属性和方法。
问题的关键:
为什么要用 List list = new ArrayList() ,而不用 ArrayList alist = new ArrayList()呢?
问题就在于List有多个实现类,现在你用的是ArrayList,也许哪一天你需要换成其它的实现类,如 LinkedList或者Vector等等,这时你只要改变这一行就行了:
List list = new LinkedList(); 其它使用了list地方的代码根本不需要改动。
假设你开始用 ArrayList alist = new ArrayList(), 这下你有的改了,特别是如果你使用了 ArrayList特有的方法和属性。可以参考:https://blog.csdn.net/sophiayang1997/article/details/78171481
Map map=new HashMap();也是这个原理
2、单例模式的使用场景,为什么要这样使用,用什么好处?
单例模式的使用场景:1、需要频繁的创建和摧毁对象,2、创建对象时耗时过多或耗费资源过多,但又经常用到的对象,3、工具类对象,4、频繁访问数据库或文件的对象。
优点:
- 系统内存中该类只存在一个对象,节省了系统资源,对于一些需要频繁创建销毁的对象,使用单例模式可以提高系统性能。
- 由于单例模式在内存中只有一个实例,减少了内存开销。
- 单例模式可以避免对资源的多重占用,例如一个写文件时,由于只有一个实例存在内存中,避免对同一个资源文件的同时写操作。
- 单例模式可以再系统设置全局的访问点,优化和共享资源访问。
- 其中使用到单例模式时,考虑较多的就是多线程的情况下如何防止被多线程同时创建等问题,其中《Head First Design Patterns》使用到“double-checked locking”来降低使用synchronization。
饿汉式与懒汉式的区别:
饿汉式是线程安全的,而懒汉式是线程不安全的,从实现方式来看,懒汉式是延时加载,在用到他的时候才会创建他,而饿汉式上来就创建好了;饿汉式,类加载时就初始化,浪费内存。
3、switch的几种类型:
基本数据类型:byte, short, char, int
包装数据类型:Byte, Short, Character, Integer
枚举类型:Enum
字符串类型:String(Jdk 7+ 开始支持)
4、Mybatis的一级缓存和二级缓存的理解和区别
一级缓存基于sqlSession默认开启,在操作数据库时需要构造SqlSession对象,在对象中有一个HashMap用于存储缓存数据。不同的SqlSession之间的缓存数据区域是互相不影响的。
一级缓存的作用域是SqlSession范围的,当在同一个sqlSession中执行两次相同的sql语句时,第一次执行完毕会将数据库中查询的数据写到缓存(内存),
第二次查询时会从缓存中获取数据,不再去底层数据库查询,从而提高查询效率。
需要注意的是,如果SqlSession执行了DML操作(增删改),并且提交到数据库,MyBatis则会清空SqlSession中的一级缓存,这样做的目的是为了保证缓存中存储的是最新的信息,避免出现脏读现象。
当一个SqlSession结束后该SqlSession中的一级缓存也就不存在了。
关闭一级缓存后,再次访问,需要再次获取一级缓存,然后才能查找数据,否则会抛出异常。
二级缓存是mapper级别的缓存。使用二级缓存时,多个SqlSession使用同一个Mapper的sql语句去操作数据库,得到的数据会存在二级缓存区域,它同样是使用HashMap进行数据存储。相比一级缓存SqlSession,二级缓存的范围更大,多个Sqlsession可以共用二级缓存,二级缓存是跨SqlSession的。
二级缓存的作用域是mapper的同一个namespace。不同的sqlSession两次执行相同的namespace下的sql语句,且向sql中传递的参数也相同,即最终执行相同的sql语句,则第一次执行完毕会将数据库中查询的数据写到缓存,第二次查询会从缓存中获取数据,不再去底层数据库查询,从而提高效率。
在MyBatis配置文件(mybatis-config.xml)中开启二级缓存(详细过程自己百度搜索开启)
//value属性默认为false
在**Mapper.xml中开启当前mapper的namespace下的二级缓存
代表创建了一个LRU缓存,并每隔60秒刷新,最大存储512个对象,而且返回的对象被认为是只读的。evicition收回策略,默认是LRU
(1)LRU最近最少使用策略,一处做长时间不被使用的对象。
(2)FIFO先进先出策略,按对象进入缓存的顺序来移除它们。
(3)SOFT软引用策略,移除基于垃圾回收器状态和软引用规则的对象。
(4)WEAK弱引用策略,更积极地移除基于垃圾收集器状态和弱引用规则的对象
5、值传递和引用传递
public static void main(String[] args) {
String name="1234";
charStriang(name);
System.out.println(name);
}
private static void charStriang(String name) {
name="welcome";
}
这个的运行结果是1234
可以参考:
https://blog.csdn.net/party3/article/details/78648186
public class TestString {
public static void main(String[] args) {
String name="1234";
charStriang(name);
System.out.println(name);
String nase=new String("99999");
charStriang(nase);
System.out.println(nase);
int a=9;
way(a);
System.out.println(9);
User user = new User("wang",10);
user(user);
System.out.println(user);
user1(user);
System.out.println(user+"--");
}
private static void user1(User user) {
user=new User("zhang",100);
System.out.println(user);
}
private static void user(User user) {
user.setName("wangbin");
}
private static void charStriang(String name) {
name="welcome"; //相当于是new String("welcome");而不是简单的赋值
}
private static void way(int name) {
name=100;
}
}
结果:
1234
99999
9
User [name=wangbin, age=10]
User [name=zhang, age=100]
User [name=wangbin, age=10]--
注意:可见在方法中对属性的修改并没有改变作为参数传递的对象的属性,因为方法中user重新指向了一个新的User()的地址,再对user的属性做修改时,作为参数传递的User对象的属性并不会发生改变,这就是我们平常对String对象的操作方法,只不过我们只常用String的简式声明方法String str = "java",这就相当于String str = new String("java"),这是创建一个新的String的过程,不单单是赋值的过程。
6、mybatis的分页插件
他在查询的时候会分成两个sql语句来执行的,一个是查询出总共有多少条数据,在查出对应的页码数据。
如下面的sql语句在执行的时候会分出两个sql:
SELECT iiid,title,detail,makeDate,paperMediaSource,infoUrl,keyWord,(SELECT imgUrl
FROM INFO_IMG img WHERE img.iiid=item.iiid) AS imgUrl
from info_item item WHERE item.makeDate<=#{expireDate} AND ChanNum='010'
AND InfoCls='001179' ORDER BY item.makeDate DESC分成:
1、SELECT count(*) FROM info_item item WHERE item.makeDate <= ? AND ChanNum = '010' AND InfoCls = '001179'
2、SELECT TOP 20 iiid, title, detail, makeDate, paperMediaSource, infoUrl, keyWord, imgUrl
FROM (SELECT ROW_NUMBER() OVER ( ORDER BY item.makeDate DESC) PAGE_ROW_NUMBER, iiid, title, detail,
makeDate, paperMediaSource, infoUrl, keyWord, (SELECT imgUrl FROM INFO_IMG img WHERE img.iiid = item.iiid)
AS imgUrl FROM info_item item WHERE item.makeDate <= '2018-08-30 23:59:59' AND ChanNum = '010' AND InfoCls = '001179')
AS PAGE_TABLE_ALIAS WHERE PAGE_ROW_NUMBER > 0 ORDER BY PAGE_ROW_NUMBER
mybatis的分页插件的原理就是实现mybatis的拦截器,对查询的操作进行拦截。
7、Executors创建多少种线程池:
https://blog.csdn.net/a491857321/article/details/78964368
1、创建可变线程池:newCachedThreadPool,这个线程池里面的线程数量是可以进行变化的,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。核心线程池大小为0,最大为Integer.MAX_VALUE,线程空闲存活时间是60秒。
2、创建固定线程池:newFixedThreadPool,核心线程数即为最大线程数,线程不会被回收,直到调用shutdown方法回收。
3、创建任务线程池:newScheduledThreadPool,该线程池可用于定时或周期性任务的执行
4、创建单例线程池:newSingleThreadExecutor,该线程池有且仅有一个线程执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
5、创建并行线程池:newWorkStealingPool,创建一个拥有多个任务队列(以便减少连接数)的线程池(jdk1.8)
线程池都有哪些属性:
ThreadPoolExecutor的完整构造方法的签名是:ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler)
.
corePoolSize - 池中所保存的线程数,包括空闲线程。
maximumPoolSize-池中允许的最大线程数。
keepAliveTime - 当线程数大于核心时,此为终止前多余的空闲线程等待新任务的最长时间。
unit - keepAliveTime 参数的时间单位。
workQueue - 执行前用于保持任务的队列。此队列仅保持由 execute方法提交的 Runnable任务。
threadFactory - 执行程序创建新线程时使用的工厂。
handler - 由于超出线程范围和队列容量而使执行被阻塞时所使用的处理程序。
8、给ArrayList排序:
总结:
- 使用Collections.sort()传入ArrayList,会采用默认的方式进行排序(字典序)
- 使用Collections.sort()传入ArrayList和自己实现Commparator接口的类的对象,实现自定义排序
- 使用List.sort()传入自己实现Commparator接口的类的对象,实现自定义排序
- Comparator返回值在jdk1.7、jdk1.8里必须是一对相反数,如1和-1,不能是1和0.因为1.7的排序算法采用timsort,对返回值有严格要求http://baike.baidu.com/link?url=UCowuf65GHz3cWVf_d7t0QzYUCcwU0QUwserNTIrImlaTBvBAaVfywzppQ70DqWKzUu3dPqsF21k9IDpT8QPE_
List<Integer> list=new ArrayList<>();
Comparator<Integer> c = new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
if(o1>o2){
return 1;
}
return -1;
}
};
list.add(78);
list.add(98);
list.add(34);
list.add(23);
list.sort(c);
list.forEach(System.out::println);
输出结果:
23
34
78
98
或者:
@SuppressWarnings("unchecked")
public static void main(String[] args) {
// TODO Auto-generated method stub
List listInt = new ArrayList(),
listStr = new ArrayList();
//自定义Comparator对象,自定义排序
Comparator c = new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
// TODO Auto-generated method stub
if((int)o1<(int)o2)
return 1;
//注意!!返回值必须是一对相反数,否则无效。jdk1.7以后就是这样。
// else return 0; //无效
else return -1;
}
};
listInt.add(2); listInt.add(4); listInt.add(9); listInt.add(5);
listStr.add("haha");
listStr.add("hehe");
listStr.add("ao");
listStr.add("Ti");
@SuppressWarnings("rawtypes")
List list01 = new ArrayList(listInt);
List list02 = new ArrayList(listInt);
Collections.sort(listInt);
Collections.sort(listStr);
list01.sort(c);
Collections.sort(list02,c);
System.out.println(listInt);
System.out.println(listStr);
System.out.println(list01);
System.out.println(list02);
}
输出:
[2, 4, 5, 9]
[Ti, ao, haha, hehe]
[9, 5, 4, 2]
[9, 5, 4, 2]
9、web容器、JMS、JNDI、JTA名词的意思
web容器:给处于其中的应用程序组件(JSP,SERVLET)提供一个环境,使JSP,SERVLET直接更容器中的环境变量接口交互,不必关注其它系统问题。主要有WEB服务器来实现。例如:TOMCAT,WEBLOGIC,WEBSPHERE等。该容器提供的接口严格遵守J2EE规范中的WEB APPLICATION 标准。我们把遵守以上标准的WEB服务器就叫做J2EE中的WEB容器。
EJB容器:Enterprise java bean 容器。更具有行业领域特色。他提供给运行在其中的组件EJB各种管理功能。只要满足J2EE规范的EJB放入该容器,马上就会被容器进行高效率的管理。并且可以通过现成的接口来获得系统级别的服务。例如邮件服务、事务管理。
JNDI:(Java Naming & Directory Interface)JAVA命名目录服务。主要提供的功能是:提供一个目录系统,让其它各地的应用程序在其上面留下自己的索引,从而满足快速查找和定位分布式应用程序的功能。
JMS:(Java Message Service)JAVA消息服务。主要实现各个应用程序之间的通讯。包括点对点和广播。
JTA:(Java Transaction API)JAVA事务服务。提供各种分布式事务服务。应用程序只需调用其提供的接口即可。
JAF:(Java Action FrameWork)JAVA安全认证框架。提供一些安全控制方面的框架。让开发者通过各种部署和自定义实现自己的个性安全控制策略。
RMI/IIOP:(Remote Method Invocation /internet对象请求中介协议)他们主要用于通过远程调用服务。例如,远程有一台计算机上运行一个程序,它提供股票分析服务,我们可以在本地计算机上实现对其直接调用。当然这是要通过一定的规范才能在异构的系统之间进行通信。RMI是JAVA特有的。
10、Union和Union All到底有什么区别
Union:对两个结果集进行并集操作,不包括重复行,同时进行默认规则的排序;
Union All:对两个结果集进行并集操作,包括重复行,不进行排序;
11、重写方法需要注意的点是什么?
重写规则之一:重写方法不能比被重写方法限制有更严格的访问级别。(也就是说子类的访问修饰符不能比父类的访问修饰符低,如:父类的方法是protected修饰的,那么子类重写这个方法的访问修饰符一定是protected/problic修饰的)
(但是可以更广泛,比如父类方法是包访问权限,子类的重写方法是public访问权限。)比如:Object类有个toString()方法,开始重写这个方法的时候我们总容易忘记public修饰符,编译器当然不会放过任何教训我们的机会。出错的原因就是:没有加任何访问修饰符的方法具有包访问权限,包访问权限比public当然要严格了,所以编译器会报错的。
重写规则之二: 参数列表必须与被重写方法的相同。
重写有个孪生的弟弟叫重载,也就是后面要出场的。如果子类方法的参数与父类对应的方法不同,那么就是你认错人了,那是重载,不是重写。重写规则之三:返回类型必须与被重写方法的返回类型相同。
父类方法A:void eat(){} 子类方法B:int eat(){}两者虽然参数相同,可是返回类型不同,所以不是重写。
父类方法A:int eat(){} 子类方法B:long eat(){}返回类型虽然兼容父类,但是不同就是不同,所以不是重写。重写规则之四:重写方法不能抛出新的异常或者比被重写方法声明的检查异常更广的检查异常。但是可以抛出更少,更有限或者不抛出异常。
注意:这种限制只是针对检查异常,至于运行时异常RuntimeException及其子类不再这个限制之中。重写规则之五: 不能重写被标识为final的方法。
重写规则之六:如果一个方法不能被继承,则不能重写它。如private方法
比较典型的就是父类的private方法。下例会产生一个有趣的现象。
重写规则之七:子类不能用 静态方法 重写父类的非静态方法
重写规则之八:子类不能用非静态方法 重写 父类的静态方法
13、servlet处理请求的方式?
是以线程的方式
14、Integer与int
public static void main(String[] args) {
Integer i1=new Integer(130);
Integer i2=130;
int i3=130;
System.out.println(i1==i2);
System.out.println(i3==i1);
System.out.println(i1.equals(i2));
}
结果:
false
true
true
产生这个结果的原因:
1、因为Integer是一个包装类型的new出来的对象存放在堆里面,而i1只是一个引用而已,而i2是直接指向常量池的,引用是不一样的,所有不相等,而int和Integer在比较的时候都是比的是值。
总结Integer与int的区别:
1 int与Integer的基本使用对比
(1)Integer是int的包装类;int是基本数据类型;
(2)Integer变量必须实例化后才能使用;int变量不需要;
(3)Integer实际是对象的引用,指向此new的Integer对象;int是直接存储数据值 ;
(4)Integer的默认值是null;int的默认值是0。
2 int与Integer的深入对比
(1)由于Integer变量实际上是对一个Integer对象的引用,所以两个通过new生成的Integer变量永远是不相等的(因为new生成的是两个对象,其内存地址不同)。
Integer i = new Integer(100); Integer j = new Integer(100); System.out.print(i == j); //false
(2)Integer变量和int变量比较时,只要两个变量的值是向等的,则结果为true(因为包装类Integer和基本数据类型int比较时,java会自动拆包装为int,然后进行比较,实际上就变为两个int变量的比较)
Integer i = new Integer(100); int j = 100; System.out.print(i == j); //true
(3)非new生成的Integer变量和new Integer()生成的变量比较时,结果为false。(因为非new生成的Integer变量指向的是java常量池中的对象,而new Integer()生成的变量指向堆中新建的对象,两者在内存中的地址不同)
Integer i = new Integer(100); Integer j = 100; System.out.print(i == j); //false
(4)对于两个非new生成的Integer对象,进行比较时,如果两个变量的值在区间-128到127之间,则比较结果为true,如果两个变量的值不在此区间,则比较结果为false
Integer i = 100; Integer j = 100; System.out.print(i == j); //true Integer i = 128; Integer j = 128; System.out.print(i == j); //false
对于第4条的原因: java在编译Integer i = 100 ;时,会翻译成为Integer i = Integer.valueOf(100)。而java API中对Integer类型的valueOf的定义如下,对于-128到127之间的数,会进行缓存,Integer i = 127时,会将127进行缓存,下次再写Integer j = 127时,就会直接从缓存中取,就不会new了。
public static Integer valueOf(int i){ assert IntegerCache.high >= 127; if (i >= IntegerCache.low && i <= IntegerCache.high){ return IntegerCache.cache[i + (-IntegerCache.low)]; } return new Integer(i); }
15、java中什么是虚函数?
虚函数的存在是为了多态。
C++中普通成员函数加上virtual关键字就成为虚函数
Java中其实没有虚函数的概念,它的普通函数就相当于C++的虚函数,动态绑定是Java的默认行为。如果Java中不希望某个函数具有虚函数特性,可以加上final关键字变成非虚函数
PS: 其实C++和Java在虚函数的观点大同小异,异曲同工罢了。
16、Java语言中,负责并发管理的机制是?
是多线程(多线程是Java程序的并发机制,它能同步共享数、处理不同的事件)
17、java标识符
使用标识符时,需要遵守几条规则:
1. 标识符可以由字母,数字,下划线(_),美元($)组成,但是不能包含@,%,空格等其他的特殊符号,不能以数字开头。例如 123name 就是不合法的
2.标识符不能是Java关键字和保留字(Java预留的关键字,或者以后升级版本中有可能作为关键字),但可以包含关键字和保留字~例如:不可以使用void 作为标识符,但是Myvoid 可以
3.标识符是严格却分大小写的,所以一定要分清alibaba和ALIbaba是两个不同的标识符哦
4.标识符的命名最好能反应出其作用,做到见名知意
18、单向链表反转
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class Node {
private Integer nodeId;
private Node next;
}
public static void main(String[] args) {
Node node = reverse(initNode());
System.out.println(node);
}
//第一种方式:
public static Node reverse(Node node){
Node build = Node.builder().next(null).nodeId(-1).build();
Node next = node.getNext();
while(next !=null){
Node rem = next.getNext();
next.setNext(build.getNext());
build.setNext(next);
next=rem;
}
return build;
}
public static Node initNode() {
Node node = new Node();
node.setNodeId(0);
node.setNext(Node.builder().nodeId(1).
next(Node.builder().nodeId(2).next(Node.builder().nodeId(3).
next(Node.builder().nodeId(4).next(null).build()).build()).build()).build());
return node;
}
//第二种方式
// 1.就地反转法
public static Node reverseList1(Node head) {
if (head == null){
return head;
}
Node dummy = new Node(-1,null);
dummy.setNext(head) ;
System.out.println(dummy);
Node prev = dummy.getNext();
Node pCur = prev.getNext();
while (pCur != null) {
prev.setNext(pCur.getNext());
pCur.setNext(dummy.getNext());
dummy.setNext(pCur);
pCur = prev.getNext();
}
return dummy;
}