Java笔经1:泛型,IO体系,异常,关于System类

一、泛型

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() 取得当前系统的全部属性

猜你喜欢

转载自blog.csdn.net/DragonFreedom/article/details/82527029