注:这是本人笔试期间总结的一些java知识点,太简单的没写,太深奥的疏漏较多,主要是针对较难易错点的的一些探讨,错误之处还望留言斧正。
一、泛型
1、一个简单的泛型类
- 注意<T>的位置,T可以是任意的字母,常用的有K、V、T、S、U等,多个类型变量变量可以用逗号分割Pair<T, K, V>
class Pair<T> {
private T min;
public Pair() {
this.min = null;
}
public Pair(T min) {
this.min = min;
}
public T getMin() {
return min;
}
public void setMin(T min) {
this.min = min;
}
}
2、泛型方法
- 下边是一个简单的泛型方法,注意类型变量(<T>)放在修饰符的后边(public、static),在返回值类型前边(T)。
public static <T> T getMiddle(T... ts) {
return ts[ts.length / 2];
}
- 对此方法的调用也有两种形式,一种指定类型变量,另一种编译器可以根据参数推断
ArrayAlg.<Integer>getMiddle(1, 2);
ArrayAlg.getMiddle(1, 2);
- 当没有指定类型变量,并且根据参数推断时发现不是同一种类型,会继续向上推断,直至找到共同的父类,这个父类就是推断出来的类型变量了,如下,第一行推断结果为Number,第二行为Object
Number n = ArrayAlg.getMiddle(1, 2, 2.2);
Object o = ArrayAlg.getMiddle(1, 2.2, "hello");
3、泛型变量限定
- 为什么要限定泛型变量?如果在方法中需要调用T类型的对象的一个方法,但是这个方法有的类有有的类没有,那么就限制这个类型变量必须是指定有这个方法的子类,包括继承和实现接口,如下将T类型变量限制为实现Comparable接口的类,这样就可以调T类型变量的compareTo()方法而不会报错了。
public static <T extends Comparable<T>> T min(T[] ts) {
if (ts == null || ts.length == 0)
return null;
T min = null;
for (T t : ts)
min = min.compareTo(t) > 0 ? t : min;
return min;
}
- 语法为<T extends BoundingType>,接口和继承都用extends,多限制可以用&分割<T extends BoundingType1 & BoundingType2>
4、泛型代码和虚拟机
- 编译的时候类型参数会被擦除掉,替换为限定的类型,没有限定类型的话替换为Object,注意:限定类型是指<T extends BoundingType>中的BoundingType,而不是实例化时像List<String>中的String
- 如果有多个限定,会选择第一个限定作为擦除后的替换,例如在上边的例子中将类型限定改为<T extends Comparable & Serializable>,将会采用Comparable来替换T,但是如果写成<T extends Serializable & Comparable>,将会使用Serializable来替换T,但是之后的代码中会执行compareTo()方法,编译器就要做必要的类型转化了,所以为了提高效率,应将标签接口(没有方法的接口)放到边界列表的末尾(边界列表就是指上边的多个类型限定)
- 在擦除的的时候会有一些必要的强制类型转化,其中脉络比较复杂,也牵扯了泛型的核心,这里不展开说明了。
5、通配符
- 用如下代码讲解通配符的作用和简单用法
public class Main {
public static void main(String[] args) {
Utils.printEmployee(new Pair(new Manager("Job"), new Manager("Ketty")));
}
}
class Utils {
// 方法一:不能接收Manager
public static void printEmployee(Pair<Employee> e) {
System.out.println("first:" + e.getFirst().getName());
System.out.println("second:" + e.getSeconde().getName());
}
// 方法二:能接收Manager
public static void printEmployee(Pair<? extends Employee> e) {
System.out.println("first:" + e.getFirst().getName());
System.out.println("second:" + e.getSeconde().getName());
}
}
class Pair<T> {
private T first;
private T seconde;
public Pair(T first, T second) {
this.first = first;
this.seconde = second;
}
// getter and setter...
}
class Employee {
private String name;
public Employee(String name) {
this.name = name;
}
// getter and setter...
}
class Manager extends Employee {
private int rank;
public Manager(String name) {
super(name);
}
// getter and setter...
}
- Manager继承自Employee,工具类中有一个方法printEmployee()希望可以去打印Pair类中存放的两个Employee的name信息,自然也可以想到,Manager继承自Employee,那么应该也可以打印Manager的name信息
- 那么明确一点Pair<Manager>不是Pair<Employee>的子类,而且他们没有任何关系
- 那么问题来了根据上一点说的,Pair<Manager>不是Pair<Employee>的子类,那方法一 printEmployee(Pair<Employee> e) 也就没法接收Pair<Manager>类型的参数,那么main方法中的调用也就自然而然报错,这就不符合我们自然的想法了,很不舒服,怎么解决呢?用通配符。
- 方法二 printEmployee(Pair<? extends Employee> e) 需要的参数是Pair<? extends Employee>类型,而Pair<Manager>是其子类,所以可以成功。
二、Java的IO结构
1、InputStream、OutputStream、Reader、Writer常用类继承类图
2、比较
- InputStream 和 OutputStream用于字节的操作,Writer 和 Reader用于字符操作,File和RandomAccessFile用于磁盘操作,Socket用于网络操作
三、异常体系
1、异常继承类图
2、Error和Exception
- Error程序无法处理,JVM一般会选择将线程终,而Exception是需要程序去处理的,又可以分为
3、运行时异常和非运行时异常
- RuntimeException类及其子类为运行时异常,不需要程序处理
- Exception中出除去RuntimeException剩下的都是非运行时异常,还有用户自定义的异常也是非运行时异常,需要捕获或者抛出处理
4、try、catch、finally
public class Main {
public static void main(String[] args) {
System.out.println(test02());
}
public static int test02() {
int[] nums = new int[] {1,2};
try {
System.out.println("try...");
System.out.println(nums[1]);
// System.out.println(nums[2]);
return 0;
} catch (Exception e) {
System.out.println("catch...");
return 1;
}finally {
System.out.println("finally...");
return 2;
}
}
}
- 以上代码是一个比较经典的异常返回的例子,如果在try、catch、finally中都做return,到底谁有效,分别访问nums数组的第1个和第2个元素,形成有异常和没有异常两种情况,发现main方法中打印的分别是2和1,没有0,说明最后一个return起作用
5、catch匹配
- 多个catch存在时,按顺序进行匹配,匹配到之后执行此catch块,并且只执行这一个catch块
6、throw和throws
- throw用于方法内部抛出一个异常,注意是一个,没用复数就可以看出来
- throws用于方法抛出异常,方法可能抛出多种异常,用复数
四、System类
- System类中所有的属性都是静态的
- public static void exit(int status) 系统退出 ,如果status为0就表示退出
- public static void arraycopy(Object src,int srcPos, Object dest,int desPos,int length) 数组拷贝操作,如果是数组比较大,那么使用此方法是非常有效的,因为其使用的是内存复制,省去了大量的数组寻址访问等时间。Arrays有个方法copyOf(),其内部也是用arraycopy()实现的,源码如下
public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
@SuppressWarnings("unchecked")
T[] copy = ((Object)newType == (Object)Object[].class) ? (T[]) new Object[newLength] :(T[]) Array.newInstance(newType.getComponentType(), newLength);
System.arraycopy(original, 0, copy, 0, Math.min(original.length, newLength));
return copy;
}
- public static void gc() 运行垃圾收集机制,调用的是Runtime类中的gc方法
public static Runtime getRuntime() {
return currentRuntime;
}
- public static Properties getProperties() 取得当前系统的全部属性