咱们知道finally{}中的语句是必定会执行的,那么这个可能正常脱口而出就是return以前,return以后可能就出了这个方法了,鬼知道跑哪里去了,但更准确的应该是在return中间执行,请看下面程序代码的运行结果:java
public classTest { public static void main(String[]args) { System.out.println(newTest().test());; } static int test() { intx = 1; try { return x; } finally { ++x; } } }
执行结果以下:mysql
1
运行结果是1,为何呢?主函数调用子函数并获得结果的过程,比如主函数准备一个空罐子,当子函数要返回结果时,先把结果放在罐子里,而后再将程序逻辑返回到主函数。所谓返回,就是子函数说,我不运行了,你主函数继续运行吧,这没什么结果可言,结果是在说这话以前放进罐子里的。面试
异常表示程序运行过程当中可能出现的非正常状态,运行时异常表示虚拟机的一般操做中可能遇到的异常,是一种常见运行错误。java编译器要求方法必须声明抛出可能发生的非运行时异常,可是并不要求必须声明抛出未被捕获的运行时异常。算法
24W字Java面试手册下载地址: https://shimo.im/docs/Wyj8QRp...
24W字Java面试手册下载地址: https://shimo.im/docs/Wyj8QRp...
24W字Java面试手册下载地址: https://shimo.im/docs/Wyj8QRp...
error 表示恢复不是不可能但很困难的状况下的一种严重问题。好比说内存溢出。不可能期望程序能处理这样的状况。exception表示一种设计或实现问题。也就是说,它表示若是程序运行正常,从不会发生的状况。sql
异常是指java程序运行时(非编译)所发生的非正常状况或错误,与现实生活中的事件很类似,现实生活中的事件能够包含事件发生的时间、地点、人物、情节等信息,能够用一个对象来表示,Java使用面向对象的方式来处理异常,它把程序中发生的每一个异常也都分别封装到一个对象来表示的,该对象中包含有异常的信息。编程
Java对异常进行了分类,不一样类型的异常分别用不一样的Java类表示,全部异常的根类为java.lang.Throwable。数组
Throwable下面又派生了两个子类:缓存
系统异常是软件自己缺陷所致使的问题,也就是软件开发人员考虑不周所致使的问题,软件使用者没法克服和恢复这种问题,但在这种问题下还可让软件系统继续运行或者让软件挂掉,例如,数组脚本越界(ArrayIndexOutOfBoundsException),空指针异常(NullPointerException)、类转换异常(ClassCastException);安全
普通异常是运行环境的变化或异常所致使的问题,是用户可以克服的问题,例如,网络断线,硬盘空间不够,发生这样的异常后,程序不该该死掉。服务器
java为系统异常和普通异常提供了不一样的解决方案,编译器强制普通异常必须try..catch处理或用throws声明继续抛给上层调用方法处理,因此普通异常也称为checked异常,而系统异常能够处理也能够不处理,因此,编译器不强制用try..catch处理或用throws声明,因此系统异常也称为unchecked异常。
"=="
对于基本类型和引用类型 == 的做用效果是不一样的,以下所示:
String x = "string"; String y = "string"; String z = new String("string"); System.out.println(x==y); // true System.out.println(x==z); // false System.out.println(x.equals(y)); // true System.out.println(x.equals(z)); // true
由于 x 和 y 指向的是同一个引用,因此 == 也是 true,而 new String()方法则重写开辟了内存空间,因此 == 结果为 false,而 equals 比较的一直是值,因此结果都为 true。
equals
equals 本质上就是 ==,只不过 String 和 Integer 等重写了 equals 方法,把它变成了值比较。看下面的代码就明白了。
首先来看默认状况下 equals 比较一个有相同值的对象,代码以下:
class Cat { public Cat(String name) { this.name = name; } private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } } Cat c1 = new Cat("叶痕秋"); Cat c2 = new Cat("叶痕秋"); System.out.println(c1.equals(c2)); // false
输出结果出乎咱们的意料,居然是 false?这是怎么回事,看了 equals 源码就知道了,源码以下:
public boolean equals(Object obj) { return (this == obj); }
原来 equals 本质上就是 ==。
那问题来了,两个相同值的 String 对象,为何返回的是 true?代码以下:
String s1 = new String("叶子"); String s2 = new String("叶子"); System.out.println(s1.equals(s2)); // true
一样的,当咱们进入 String 的 equals 方法,找到了答案,代码以下:
public boolean equals(Object anObject) { if (this == anObject) { return true; } if (anObject instanceof String) { String anotherString = (String)anObject; int n = value.length; if (n == anotherString.value.length) { char v1[] = value; char v2[] = anotherString.value; int i = 0; while (n-- != 0) { if (v1[i] != v2[i]) return false; i++; } return true; } } return false; }
原来是 String 重写了 Object 的 equals 方法,把引用比较改为了值比较。
总结
== 对于基本类型来讲是值比较,对于引用类型来讲是比较的是引用;而 equals 默认状况下是引用比较,只是不少类从新了 equals 方法,好比 String、Integer 等把它变成了值比较,因此通常状况下 equals 比较的是值是否相等。
java的集合有两类,一类是List,还有一类是Set。前者有序可重复,后者无序不重复。当咱们在set中插入的时候怎么判断是否已经存在该元素呢,能够经过equals方法。可是若是元素太多,用这样的方法就会比较满。
因而有人发明了哈希算法来提升集合中查找元素的效率。 这种方式将集合分红若干个存储区域,每一个对象能够计算出一个哈希码,能够将哈希码分组,每组分别对应某个存储区域,根据一个对象的哈希码就能够肯定该对象应该存储的那个区域。
hashCode方法能够这样理解:它返回的就是根据对象的内存地址换算出的一个值。这样一来,当集合要添加新的元素时,先调用这个元素的hashCode方法,就一会儿能定位到它应该放置的物理位置上。若是这个位置上没有元素,它就能够直接存储在这个位置上,不用再进行任何比较了;若是这个位置上已经有元素了,就调用它的equals方法与新元素进行比较,相同的话就不存了,不相同就散列其它的地址。这样一来实际调用equals方法的次数就大大下降了,几乎只须要一两次。
24W字Java面试手册下载地址: https://shimo.im/docs/Wyj8QRp...
24W字Java面试手册下载地址: https://shimo.im/docs/Wyj8QRp...
24W字Java面试手册下载地址: https://shimo.im/docs/Wyj8QRp...
不对,两个对象的 hashCode() 相同,equals() 不必定 true。
代码示例:
String str1 = "keep"; String str2 = "brother"; System. out. println(String. format("str1:%d | str2:%d", str1. hashCode(),str2. hashCode())); System. out. println(str1. equals(str2));
执行结果:
str1:1179395 | str2:1179395 false
代码解读:很显然“keep”和“brother”的 hashCode() 相同,然而 equals() 则为 false,由于在散列表中,hashCode() 相等即两个键值对的哈希值相等,然而哈希值相等,并不必定能得出键值对相等。
泛型是Java SE 1.5以后的特性, 《Java 核心技术》中对泛型的定义是:
“泛型” 意味着编写的代码能够被不一样类型的对象所重用。
“泛型”,顾名思义,“泛指的类型”。咱们提供了泛指的概念,但具体执行的时候却能够有具体的规则来约束,好比咱们用的很是多的ArrayList就是个泛型类,ArrayList做为集合能够存放各类元素,如Integer, String,自定义的各类类型等,但在咱们使用的时候经过具体的规则来约束,如咱们能够约束集合中只存放Integer类型的元素,如
List<Integer> iniData = new ArrayList<>()
使用泛型的好处?
以集合来举例,使用泛型的好处是咱们没必要由于添加元素类型的不一样而定义不一样类型的集合,如整型集合类,浮点型集合类,字符串集合类,咱们能够定义一个集合来存放整型、浮点型,字符串型数据,而这并非最重要的,由于咱们只要把底层存储设置了Object便可,添加的数据所有均可向上转型为Object。 更重要的是咱们能够经过规则按照本身的想法控制存储的数据类型。
面向对象的编程语言有封装、继承 、抽象、多态等4个主要的特征。
注意:在使用多态后的父类引用变量调用方法时,会调用子类重写后的方法。
定义格式:父类类型 变量名=new 子类类型();
重写(Override)
从字面上看,重写就是 从新写一遍的意思。其实就是在子类中把父类自己有的方法从新写一遍。子类继承了父类原有的方法,但有时子类并不想原封不动的继承父类中的某个方法,因此在方法名,参数列表,返回类型(除过子类中方法的返回值是父类中方法返回值的子类时)都相同的状况下, 对方法体进行修改或重写,这就是重写。但要注意子类函数的访问修饰权限不能少于父类的。
public class Father { public static void main(String[] args) { // TODO Auto-generated method stub Son s = new Son(); s.sayHello(); } public void sayHello() { System.out.println("Hello"); } } class Son extends Father{ @Override public void sayHello() { // TODO Auto-generated method stub System.out.println("hello by "); } }
重写 总结:
1.发生在父类与子类之间
2.方法名,参数列表,返回类型(除过子类中方法的返回类型是父类中返回类型的子类)必须相同
3.访问修饰符的限制必定要大于被重写方法的访问修饰符(public>protected>default>private)
4.重写方法必定不能抛出新的检查异常或者比被重写方法申明更加宽泛的检查型异常
重载(Overload)
在一个类中,同名的方法若是有不一样的参数列表(参数类型不一样、参数个数不一样甚至是参数顺序不一样)则视为重载。同时,重载对返回类型没有要求,能够相同也能够不一样,但不能经过返回类型是否相同来判断重载。
public class Father { public static void main(String[] args) { // TODO Auto-generated method stub Father s = new Father(); s.sayHello(); s.sayHello("wintershii"); } public void sayHello() { System.out.println("Hello"); } public void sayHello(String name) { System.out.println("Hello" + " " + name); } }
重载 总结:
1.重载Overload是一个类中多态性的一种表现
2.重载要求同名方法的参数列表不一样(参数类型,参数个数甚至是参数顺序)
3.重载的时候,返回值类型能够相同也能够不相同。没法以返回型别做为重载函数的区分标准
24W字Java面试手册下载地址: https://shimo.im/docs/Wyj8QRp...
24W字Java面试手册下载地址: https://shimo.im/docs/Wyj8QRp...
24W字Java面试手册下载地址: https://shimo.im/docs/Wyj8QRp...
java中提供了如下四种建立对象的方式:
public class Test { public static void main(String[] args) { ArrayList<Integer> list = new ArrayList<Integer>(); list.add(2); Iterator<Integer> iterator = list.iterator(); while(iterator.hasNext()){ Integer integer = iterator.next(); if(integer==2) list.remove(integer); } } }
执行上段代码是有问题的,会抛出ConcurrentModificationException
异常。
缘由:调用list.remove()
方法致使modCount
和expectedModCount
的值不一致。
final void checkForComodification() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); }
解决办法:在迭代器中若是要删除元素的话,须要调用Iterator
类的remove
方法。
public class Test { public static void main(String[] args) { ArrayList<Integer> list = new ArrayList<Integer>(); list.add(2); Iterator<Integer> iterator = list.iterator(); while(iterator.hasNext()){ Integer integer = iterator.next(); if(integer==2) iterator.remove(); //注意这个地方 } } }
相同点:
不一样点:
什么是fail-fast?
就是最快的时间能把错误抛出而不是让程序执行。
Java 5提供了ConcurrentHashMap,它是HashTable的替代,比HashTable的扩展性更好。
ConcurrentHashMap将整个Map分为N个segment(相似HashTable),能够提供相同的线程安全,可是效率提高N倍,默认N为16。
HashMap能够经过下面的语句进行同步:
Map m = Collections.synchronizeMap(hashMap);
按功能来分:输入流(input)、输出流(output)。
按类型来分:字节流和字符流。
字节流和字符流的区别是:字节流按 8 位传输以字节为单位输入输出数据,字符流按 16 位传输以字符为单位输入输出数据。
一、定义:
反射机制是在运行时,对于任意一个类,都可以知道这个类的全部属性和方法;对于任意个对象,都可以调用它的任意一个方法。在java中,只要给定类的名字,就能够经过反射机制来得到类的全部信息。
这种动态获取的信息以及动态调用对象的方法的功能称为Java语言的反射机制。
二、哪里会用到反射机制?
jdbc就是典型的反射
Class.forName('com.mysql.jdbc.Driver.class');//加载MySQL的驱动类
这就是反射。如hibernate,struts等框架使用反射实现的。
24W字Java面试手册下载地址: https://shimo.im/docs/Wyj8QRp...
24W字Java面试手册下载地址: https://shimo.im/docs/Wyj8QRp...
24W字Java面试手册下载地址: https://shimo.im/docs/Wyj8QRp...
第一步:获取Class对象,有4种方法: 1)Class.forName(“类的路径”); 2)类名.class 3)对象名.getClass() 4)基本类型的包装类,能够调用包装类的Type属性来得到该包装类的Class对象
1)Class:表示正在运行的Java应用程序中的类和接口 注意: 全部获取对象的信息都须要Class类来实现。 2)Field:提供有关类和接口的属性信息,以及对它的动态访问权限。 3)Constructor:提供关于类的单个构造方法的信息以及它的访问权限 4)Method:提供类或接口中某个方法的信息
优势:
一、可以运行时动态获取类的实例,提升灵活性;
二、与动态编译结合
缺点:
一、使用反射性能较低,须要解析字节码,将内存中的对象进行解析。
解决方案:
一、经过setAccessible(true)关闭JDK的安全检查来提高反射速度;
二、屡次建立一个类的实例时,有缓存会快不少
三、ReflectASM工具类,经过字节码生成的方式加快反射速度
二、相对不安全,破坏了封装性(由于经过反射能够得到私有方法和属性)
Java Io 流共涉及 40 多个类,这些类看上去很杂乱,但实际上颇有规则,并且彼此之间存在很是紧密的联系, Java I0 流的 40 多个类都是从以下 4 个抽象类基类中派生出来的。
按操做方式分类结构图: