问题:synchronized 和 lock 的区别?
1、Lock不是Java语言内置的,synchronized是Java语言的关键字,因此是内置特性。Lock是一个类,通过这个类可以实现同步访问;
2、Lock和synchronized有一点非常大的不同,采用synchronized不需要用户去手动释放锁,当synchronized方法或者synchronized代码块执行完之后,系统会自动让线程释放对锁的占用;而Lock则必须要用户去手动释放锁(lock.unlock()),如果没有主动释放锁,就有可能导致出现死锁现象。
问题:对jsp的认识?
本质就是一个servlet,由于servlet响应页面非常麻烦,需要大量类似resp.getWrite().write("xxx")这样的类似的代码,但是servlet的一个显著的优点,就是我们可以用java逻辑代码和resp.getWrite().write()已经混和使用,同时方便数据的动态传输。但是由于缺点,我们就可以利用jsp技术来解决我们写大量的resp.getWrite().write()这样的代码
jsp文件最终是会被转成servlet的,tomcat只认识servlet。jsp里面可以封装java代码块,一个jsp文件就可以完成所有的任务(jsp中可以有java代码)。
解释:<% java代码块%> 会原样转译到jsp对应的servlet文件的_JspService方法(tomcat请求后,service会调用这个方法)中,所以只要jsp中写入正确的java代码即可。
<body> <% response.getWriter().write(request.getMethod()); %> //无论是逻辑判断,请求响应,一个servlet可以完成的,jsp都可以完成 <% int a =3; if (a>1){ %> <b>a</b><% } %> </body>
但是如果jsp中封装java代码块,不仅书写很麻烦,同时阅读也很困难。所以开发的时候,我们使用servlet做请求的逻辑处理,jsp做页面的展示。
图解jsp
jsp转成servlet时机,在请求到来的时候。第一个请求是发给org.apache.jasper.servlet.JspServlet 来处理,(个人理解:它将 jsp转成一个新的servlet,并把请求发给了这个servlet)
java面向对象的特征?
封装
把描述一个对象的属性和行为封装成一个类,把具体的业务逻辑功能实现封装成一个方法,其次封装的意义还有效的保护属性通过访问修饰符私有化属性(成员变量),公有化方法。隐藏细节等
继承
实现代码的复用,将所有子类所共有的行为和属性抽取为一个父类,所有的子类继承该类,可具备父类的属性和行为,继承具有单一性和传递性。
多态
实现条件:继承、方法重写
龙生九子,各有不同
程序中定义的引用类型变量所指向的具体类型和调用的具体方法在程序编译阶段无法确定,而是在运行期才能确定该引用类型变量指向具体哪个对象而调用在哪个类中声明的方法。java虚拟机(JVM)是根据实际引用的对象来调用方法的,而不是根据变量的类型,这一特性叫做虚拟方法调用。
多态的表现形式有强制类型转换,向上转型等,多态可分为行为多态和对象多态。
行为多态:同一个run( ){ }方法,不同的对象调用时会有不同的实现,猫调用时是跑,鱼调用时是游,鸟调用时是飞。
对象多态:同一个对象,可以被转型为不同的类型,比如同一个人对象,可以被造型为儿子,父亲,员工等。
那么,多态的作用是什么呢?我们知道,封装可以隐藏实现细节,使得代码模块化;继承可以扩展已存在的代码模块(类);它们的目的都是为了——代码重用。而多态则是为了实现另一个目的——接口重用!多态的作用,就是为了类在继承和派生的时候,保证使用“家谱”中任一类的实例的某一属性时的正确调用。
如果没有多态的一个糟糕的情况
public class Demo { public static void main(String[] args){ } public void test(A a){ a.test(); } public void test(B b){ b.test(); } } class A{ public void test(){ System.out.println("A"); } } class B{ public void test(){ System.out.println("A"); } }
int和Integer的区别?
1、Integer是int的包装类,int则是java的一种基本数据类型
2、Integer变量必须实例化后才能使用,而int变量不需要
3、Integer实际是对象的引用,当new一个Integer时,实际上是生成一个指针指向此对象;而int则是直接存储数据值
4、Integer的默认值是null,int的默认值是0
延伸:
关于Integer和int的比较
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的定义如下:
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); }
java对于-128到127之间的数,会进行缓存,Integer i = 127时,会将127进行缓存,下次再写Integer j = 127时,就会直接从缓存中取,就不会new了
重载和重写的区别?
Override方法重写:子类中出现了和父类中一模一样的方法声明,也被称为方法覆盖,方法名相同,参数列表相同,返回类型相同。
注意事项:
父类中私有方法不能被重写
子类重写父类方法时,访问权限不能更低(最好一致都是public)
父类静态方法,子类也必须通过静态方法进行重写。
Overload方法重载:同一个类中出现的方法名相同,参数列表不同的方法,与返回值无关。
抽象类和接口区别?
首先我们要明白,抽象类是对类的抽象,而接口是对行为的抽象。了解了这个概念后一切都好办。抽象类是对整个类整体进行抽象,包括属性、行为,但是接口只是对类的局部(行为)进行抽象。举个例子,猫和狗都是动物,此时我们可以把猫和狗共有的属性和行为抽取出来定义一个抽象的类---Animal 类,然后 Dog 和 Cat 类就可以继承 Animal 类,然后实现自己各自的行为和属性,它们与 Animal 存在 "is-a" 的关系;但是接口仅仅是对行为的抽象,比如鸟和飞机都会飞,但是他们飞的行为不一样,我们就可以定义一个 fly 的接口,然后鸟和飞机都可以实现此接口来完成飞的动作,但是本身鸟和飞机之间并没有什么必然的联系,接口只是定义一个实现该行为的规范而已。
具体区别:
1. 抽象类通过 abstract 关键字来定义,然后子类通过 extends 继承该抽象类后并实现相应抽象方法;接口通过 interface 关键字来定义,子类通过 implements 来实现该接口中的所有方法。
2. 抽象类中的抽象方法可以使用 public、protected、default 修饰符;接口中的抽象方法默认并只能是 public,并且成员变量默认为 public static final 修饰的,所以我们可以直接通过 接口名.成员变量 使用它。
3. 抽象类中允许有非抽象的方法和成员变量包括构造方法; 接口中的方法全是抽象的,不能有方法的实现(jdk1.8之后又默认方法和静态方法)。
4. 子类只能通过继承来实现抽象类,由于 java 中的单继承特性,就导致只能继承一个抽象类;但是子类可以实现一个或多个接口,在一定程度上,这就解决了由于单继承特性所带来的问题。
5. 从作用上来看,抽象类是为了把相同的东西提取出来,即重用;接口是为了把程序进行模块化,可以降低程序的耦合。
说说反射的用途及实现?
简而言之,通过反射,我们可以在运行时获得程序或程序集中每一个类型的成员和成员的信息。程序中一般的对象的类型都是在编译期就确定下来的,而 Java 反射机制可以动态地创建对象并调用其属性,这样的对象的类型在编译期是未知的。所以我们可以通过反射机制直接创建对象,即使这个对象的类型在编译期是未知的。
反射的核心是 JVM 在运行时才动态加载类或调用方法/访问属性,它不需要事先(写代码的时候或编译期)知道运行对象是谁。
Java 反射主要提供以下功能:
- 在运行时判断任意一个对象所属的类;
- 在运行时构造任意一个类的对象;
- 在运行时判断任意一个类所具有的成员变量和方法(通过反射甚至可以调用private方法);
- 在运行时调用任意一个对象的方法
重点:是运行时而不是编译时
反射的主要用途
很多人都认为反射在实际Java中开发应用中并不广泛,其实不然。
当我们在使用 IDE(如 EclipseIDEA)时,当我们输入一个队长或者类并向调用它的属性和方法时,一按 (“.”)点号,编译器就会自动列出她的属性或方法,这里就会用到反射。
反射最重要的用途就是开发各种通用框架。
很多框架(比如 Spring)都是配置化的(比如通过 XML文件配置 JavaBean,Action之类的),为了保证框架的通用性,他们可能根据配置文件加载不同的对象或类,调用不同的方法,这个时候就必须用到反射——运行时动态加载需要加载的对象。
举一个例子,在运用Struts 2框架的开发中我们一般会在struts.xml里去配置Action,比如:
<action name="login" class="org.ScZyhSoft.test.action.SimpleLoginAction" method="execute"> <result>/shop/shop-index.jsp</result> <result name="error">login.jsp</result> </action>
——比如我们请求login.action,那么StrutsPrepareAndExecuteFilter就会去解析struts.xml文件,检索action中name为login的Action,并根据class属性创建SimpleLoginAction实例,并用invoke方法来调用execute方法,这个过程离不开反射。
对与框架开发人员来说,反射虽小但作用非常大,它是各种容器实现的核心。而对于一般的开发者来说,不深入框架开发则用反射用的就会少一点,不过了解一下框架的底层机制有助于丰富自己的编程思想,也是很有益的。
在Java中,对象什么时候可以被垃圾回收?
当一个对象到GC Roots不可达时,在下一个垃圾回收周期中尝试回收该对象,如果该对象重写了finalize()方法,并在这个方法中成功自救(将自身赋予某个引用),那么这个对象不会被回收。但如果这个对象没有重写finalize()方法或者已经执行过这个方法,也自救失败,该对象将会被回收。
问:请问下,如果某个对象没有重写finalize()方法,当第一次检测到不可达后被标记,然后就会被回收吗?是不是重写了finalize()方法的对象被回收需要经过两部,而不重写的只需要一步就被回收了?
答:都是两步吧,第一次标记该对象后都会将对象的finalnize()方法放到一个队列中,在垃圾回收时。以此调用finalnize()方法,如果重写了finalnize()使对象可达,便去除标记。我是这么理解的,因为即使重写了finalnaize()而并没有使对象可达的化一样会被回收的嘛。
面向对象?
面向对象把一组数据结构和处理他们的方法组成对象;把相同的类型的对象归纳成类,通过类的封装隐藏内部细节;通过继承实现类的特化和分化;通过多态实现基于对象类型的动态分派;
Foreach和for循环的区别?
foreach不支持在循环中添加删除操作,因为在使用foreach循环的时候数组(集合)就已经被锁定不能被修改,否则会报出java.util.ConcurrentModificationException异常
foreach可以遍历无顺序set(集合),for循环需要遍历有序的。
Object类有哪些方法?
session和token的区别?
参考:https://www.cnblogs.com/xiaozhang2014/p/7750200.html
URL重写?
自己百度。主要就是当用户禁止了cookie,如何实现会话的一种手段。
MySQL数据库优化?
参考:https://www.cnblogs.com/lianxuebin/p/8664587.html
什么是hash,hash值,hash算法,hash碰撞?
假如有10个自然数,每一个自然数的取值在(0-10000),假如这个10个资源数分别是 (100,1000,10001..........),现在有一个数(x),判断这个x是否在这10个数中间?
方式1:我们可以使用for循环。
方式2:我们可以二分法搜索。
方式3:(重点):我们可以定义int a[] = new a[10000];,将这个10个数分别存进去(这个10个数是索引,值为1),比如a[100]=1,a[1000]=1......
所以我们只需要判断a[x] 是不是等于1,如果是,就存在,如果不是就不存在。这种方式就是最原始的hash(保证了数据的最快索引)。(有一个致命的问题就是,开辟的内存大,比如你有一个自然数10000000,那么你这个数组最小就是int[] a = new[0000000],否则不能将这个数放在数组里面)
接下来我们就得优化hash了,还是这10个数,并且这个10个数还要和索引产生有关系,如何解决呢?比如我100,我们可以将它取模(100%10)为0,这个0就从当数组的索引,a[0]=100,这种算法就是hash算法(hash算法就是算一个hash值(取模算法,所谓的取模就是为了将这个数和数组的key产生联系,当然不止有取模这一中算法,比如15^(任何一个数)的出一个结果也可以是数组的key,原因是15^任何数都小于15))。0就是hash值。
接下来我们又得考虑1000%10也是0,此时a[0]已经存在一个数了(100),此时就是hash碰撞了(我们不知当前的hash值(0),在数组中应该存什么值了)?,如何解决,我们采用一种常用的方式,使用链表,就是数组加链表。a[0]=100--->1000,这就是hashMap在jdk1.7的数据结构了。
一个hash函数,冲突越多,效率越低(数据都放在一个链表中了),
补充一致性hash:https://www.cnblogs.com/heavenhome/p/7364167.html
HashMap,HashTable,LinkedHashMap,TreeMap区别?
Map主要用于存储健值对,根据键得到值,因此不允许键重复(重复了覆盖了),但允许值重复。
Hashmap 是一个最常用的Map,它根据键的HashCode值存储数据,根据键可以直接获取它的值,具有很快的访问速度,遍历时,取得数据的顺序是完全随机的。 HashMap最多只允许一条记录的键为Null;允许多条记录的值为 Null;HashMap不支持线程的同步,即任一时刻可以有多个线程同时写HashMap;可能会导致数据的不一致。如果需要同步,可以用 Collections的synchronizedMap方法使HashMap具有同步的能力,或者使用ConcurrentHashMap。
Hashtable与 HashMap类似,它继承自Dictionary类,不同的是:它不允许记录的键或者值为空;它支持线程的同步,即任一时刻只有一个线程能写Hashtable,因此也导致了 Hashtable在写入时会比较慢。
LinkedHashMap 是HashMap的一个子类,线程不同步,保存了记录的插入顺序,在用Iterator遍历LinkedHashMap时,先得到的记录肯定是先插入的.也可以在构造时用带参数,按照应用次数排序。在遍历的时候会比HashMap慢,不过有种情况例外,当HashMap容量很大,实际数据较少时,遍历起来可能会比 LinkedHashMap慢,因为LinkedHashMap的遍历速度只和实际数据有关,和容量无关,而HashMap的遍历速度和他的容量有关。
TreeMap实现SortMap接口,能够把它保存的记录根据键排序,默认是按键值的升序排序,也可以指定排序的比较器,当用Iterator 遍历TreeMap时,得到的记录是排过序的。
一般情况下,我们用的最多的是HashMap,在Map 中插入、删除和定位元素,HashMap 是最好的选择。但如果您要按自然顺序或自定义顺序遍历键,那么TreeMap会更好。如果需要输出的顺序和输入的相同,那么用LinkedHashMap 可以实现,它还可以按读取顺序来排列.