Java 中的泛型、异常、反射机制

技术学习 / 2021-11-09

泛型机制

泛型是什么?为什么要引入泛型?

在日常开发过程中,免不了进行多类型参数的交互,而大多数情况下只是同一份代码更改了一个数据类型,如下:

private static int add(int a, int b) {
    System.out.println(a + "+" + b + "=" + (a + b));
    return a + b;
}

private static float add(float a, float b) {
    System.out.println(a + "+" + b + "=" + (a + b));
    return a + b;
}

private static double add(double a, double b) {
    System.out.println(a + "+" + b + "=" + (a + b));
    return a + b;
}

对于不同的类型,实现相同的代码需要重载三份,而这便是最让人头疼的事,于是,泛型这一概念便被引申出来了

顾名思义,泛型,广泛的类型,引入泛型的意义便在于多种数据类型执行相同的代码(即代码复用)

通过使用泛型,我们可以简化上述代码为:

private static <T extends Number> double add(T a, T b) {
    System.out.println(a + "+" + b + "=" + (a.doubleValue() + b.doubleValue()));
    return a.doubleValue() + b.doubleValue();
}

这样一个基础的泛型便实现了

泛型使用

泛型类

class Point<T>{         // T 为标识符号,可以随意指定 
    private T var;     // var 的类型由 T 指定,即:由外部指定  
    public T getVar(){  // 返回值的类型由外部决定  
        return var;  
    }  
    public void setVar(T var){  // 设置的类型也由外部决定  
        this.var = var;  
    }  
}  
public class GenericsDemo06{  
    public static void main(String args[]){  
        Point<String> p = new Point<String>();     // T 为 String 类型  
        p.setVar("it");                            // 设置字符串  
        System.out.println(p.getVar().length());   // 取得字符串的长度 
    }  
}
  

多元泛型

class Notepad<K,V>{ 
    private K key; 
    private V value; 
    public K getKey(){  
        return this.key;  
    }  
    public V getValue(){  
        return this.value;  
    }  
    public void setKey(K key){  
        this.key = key;  
    }  
    public void setValue(V value){  
        this.value = value;  
    }  
} 
public class GenericsDemo09{  
    public static void main(String args[]){  
        Notepad<String,Integer> t = null;
        t = new Notepad<String,Integer>();       // K 为 String,V 为 Integer  
        t.setKey("汤姆");        // 设置第一个内容  
        t.setValue(20);            // 设置第二个内容  
        System.out.print("姓名;" + t.getKey());      // 取得信息  
        System.out.print(",年龄;" + t.getValue());       // 取得信息  
  
    }  
}
  

泛型接口

// 泛型化接口与最大值
public class GenericComparison<T extends Comparable<T>> implements Maximum<T> {
    @Override
    public T getMax(T[] array) {
        if (array == null || array.length == 0) {
            return null;
        }
        T max = array[0];
        for (int i = 1; i < array.length; i++) {
            if (max.compareTo(array[i]) < 0) {
                max = array[i];
            }
        }
        return max;
    }
}

泛型方法

// 泛型方法与最小值
public class GenericComparison {
    public static <T extends Comparable<T>> T getMin(T[] array) {
        if (array == null || array.length == 0) {
            return null;
        }
        T min = array[0];
        for (int i = 1; i < array.length; i++) {
            if (min.compareTo(array[i]) > 0) {
                min = array[i];
            }
        }
        return min;
    }
}

泛型数组

// 自定义泛型化数组类
public class GenericArray<T> {
    private T[] array;
    private int size;
    @SuppressWarnings("unchecked")
    public GenericArray(Class<T> type, int size) {
        this.size = size;
        array = (T[]) Array.newInstance(type, size);
    }
    public void put(int index, T item) {
        if (size > index) {
            array[index] = item;
        }
    }
    public T get(int index) {
        if (size > index) {
            return array[index];
        } else {
            return null;
        }
    }
}

通配符

// 使用通配符增强泛型
public class WildcardsTest {
    public static Object getMiddle(List<? extends Number> list) {
        return list.get(list.size() / 2);
    }

    public static void main(String[] args) {
        List<Integer> ints = new ArrayList<Integer>();
        ints.add(1);
        ints.add(2);
        ints.add(3);
        System.out.println("整型列表的元素:");
        System.out.println(Arrays.toString(ints.toArray()));
        System.out.println("整型列表的中间数:" + getMiddle(ints));
        List<Double> doubles = new ArrayList<Double>();
        doubles.add(1.1);
        doubles.add(2.2);
        doubles.add(3.3);
        System.out.println("浮点列表的元素:");
        System.out.println(Arrays.toString(doubles.toArray()));
        System.out.println("浮点列表的中间数:" + getMiddle(doubles));
    }
}

伪泛型

java 中的泛型是伪泛型,原因是在编译阶段会进行类型擦除,即将所有的泛型表示都替换为具体的类型

  • 当类定义中的类型参数没有任何限制时,在类型擦除中直接被替换为Object,即形如<T><?>的类型参数都被替换为Object。
  • 当类定义中的类型参数存在限制(上下界)时,在类型擦除中替换为类型参数的上界或者下界,比如形如<T extends Number><? extends Number>的类型参数被替换为Number<? super Number>被替换为Object。
  • 擦除方法定义中的类型参数原则和擦除类定义中的类型参数是一样的

异常机制

层次结构

异常类的层次结构

  • 检查型异常(checked exceptions)

    一定情况下发生可以预计,一旦发生这种异常,必须采取某种方式进行处理

    当程序中可能出现此类异常,要么使用 try - catch 语句进行捕获,要么用 throws 语句声明抛出,否则不通过编译

  • 未检查型异常(unchecked exceptions)

    编译器不要求强制处置

    包括运行时异常(RuntimeException与其子类)和错误(Error)

处理异常

抛出异常

// 方法中抛出异常
public class ThrowException {
    public static void throwException() {
        throw new UnsupportedOperationException("方法尚未实现");
    }

    public static void main(String[] args) {
        ThrowException.throwException();
    }
}



// 方法上抛出异常
public class ThrowsException {
    public static void throwsException() throws ClassNotFoundException {
        Class.forName("com.mysql.jdbc.Driver");
    }

    public static void main(String[] args) {
        try {
            ThrowsException.throwsException();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

异常捕获

// 捕获单个异常
public class CatchException {
    public static void main(String[] args) {
        try {
            System.out.println("进入 try 块");
            @SuppressWarnings("unused")
            Class<?> clazz = Class.forName("");
            System.out.println("离开 try 块");
        } catch (ClassNotFoundException e) {
            System.out.println("进入 catch 块");
            e.printStackTrace();
            System.out.println("离开 catch 块");
        } finally {
            System.out.println("进入 finally 块");
        }
    }
}



// 捕获多个异常
public class CatchExceptions {
    private static String URL = "jdbc:mysql://localhost:3306/db_database";
    private static String DRIVER = "com.mysql.jdbc.Driver";
    private static String USERNAME = "ls";
    private static String PASSWORD = "lsilencej";
    private static Connection conn;
    private static Connection getConnection() {
        try {
            Class.forName("DRIVER");
            conn = DriverManager.getConnection(URL, USERNAME, PASSWORD);
            return conn;
        } catch (ClassNotFoundException | SQLException e) {
            e.printStackTrace();
        }
        return null;
    }

    public static void main(String[] args) {
        CatchExceptions.getConnection();
    }
}

常见异常

  • RuntimeException

    • java.lang.ArrayIndexOutOfBoundsException 数组索引越界异常。当对数组的索引值为负数或大于等于数组大小时抛出。
    • java.lang.ArithmeticException 算术条件异常。譬如:整数除零等。
    • java.lang.NullPointerException 空指针异常。当应用试图在要求使用对象的地方使用了null时,抛出该异常。譬如:调用null对象的实例方法、访问null对象的属性、计算null对象的长度、使用throw语句抛出null等等
    • java.lang.ClassNotFoundException 找不到类异常。当应用试图根据字符串形式的类名构造类,而在遍历CLASSPAH之后找不到对应名称的class文件时,抛出该异常。
    • java.lang.NegativeArraySizeException 数组长度为负异常
    • java.lang.ArrayStoreException 数组中包含不兼容的值抛出的异常
    • java.lang.SecurityException 安全性异常
    • java.lang.IllegalArgumentException 非法参数异常
  • IOException

    • IOException:操作输入流和输出流时可能出现的异常。
    • EOFException 文件已结束异常
    • FileNotFoundException 文件未找到异常
  • 其他

    • ClassCastException 类型转换异常类

    • ArrayStoreException 数组中包含不兼容的值抛出的异常

    • SQLException 操作数据库异常类

    • NoSuchFieldException 字段未找到异常

    • NoSuchMethodException 方法未找到抛出的异常

    • NumberFormatException 字符串转换为数字抛出的异常

    • StringIndexOutOfBoundsException 字符串索引超出范围抛出的异常

    • IllegalAccessException 不允许访问某类异常

    • InstantiationException 当应用程序试图使用Class类中的newInstance()方法创建一个类的实例,而指定的类对象无法被实例化时,抛出该异常

反射机制

反射是什么?

反射就是把 java 类中的各种成分映射成一个个的 java 对象

映射需要中介

这个中介即是 Class 类

Class 类实例化

// 实例化 Class 的方法
public class ClassTest {
    @SuppressWarnings("unchecked")
    public static void main(String[] args) throws ClassNotFoundException {
        System.out.println("第 1 种方法:Object.getClass()");
        Class c1 = new Date().getClass();
        System.out.println(c1.getName());
        System.out.println("第 2 种方法:.class 语法");
        Class c2 = boolean.class;
        System.out.println(c2.getName());
        System.out.println("第 3 种方法:Class.forName()");
        Class c3 = Class.forName("java.lang.String");
        System.out.println(c3.getName());
        System.out.println("第 4 种方法:包装类的 TYPE 域");
        Class c4 = Double.TYPE;
        System.out.println(c4.getName());
    }
}



// 获得 Class 对象表示实体的名称
public class ClassNameTest {
    public static void main(String[] args) {
        String dateName = new Date().getClass().getName();
        System.out.println("非数组引用类型的名称:" + dateName);
        String byteName = byte.class.getName();
        System.out.println("基本类型的名称:" + byteName);
        String oneDimensionArray = new Date[4].getClass().getName();
        System.out.println("一维引用类型数组:" + oneDimensionArray);
        String twoDimensionArray = new int[4][4].getClass().getName();
        System.out.println("二维基本类型数组:" + twoDimensionArray);
    }
}

Class 类方法

方法名 说明
forName() (1)获取Class对象的一个引用,但引用的类还没有加载(该类的第一个对象没有生成)就加载了这个类。(2)为了产生Class引用,forName()立即就进行了初始化。
Object.getClass() 获取Class对象的一个引用,返回表示该对象的实际类型的Class引用。
getName() 取完整的类名(包括包名)。
getSimpleName() 获取简单类名(不包括包名)
getCanonicalName() 获取标准类名(包括包名)
isInterface() 判断Class对象是否是表示一个接口
getInterfaces() 返回Class对象数组,表示Class对象所引用的类所实现的所有接口。
getSupercalss() 返回Class对象,表示Class对象所引用的类所继承的直接基类。应用该方法可在运行时发现一个对象完整的继承结构。
newInstance() 返回一个Oject对象,是实现“虚拟构造器”的一种途径。使用该方法创建的类,必须带有无参的构造器。
getFields() 获得某个类的所有的公共(public)的字段,包括继承自父类的所有公共字段。
getMethods() 获得某个类的所有的公共(public)的方法,包括继承自父类的所有公共方法。
getConstructors() 获得某个类的所有的公共(public)的构造器,包括继承自父类的所有公共构造器。
getAnnotations() 获得某个类的所有的注解。
getDeclaredFields() 获得某个类的非继承的字段,即包括public、private和proteced,默认但是不包括父类声明的任何字段。
getDeclaredMethods() 获得某个类的非继承的方法,即包括public、private和proteced,默认但是不包括父类声明的任何方法。
getDeclaredConstructors() 获得某个类的非继承的构造器,即包括public、private和proteced,默认但是不包括父类声明的任何构造器。

反射使用

查看类的声明

// 查看类的声明
public class ClassDeclarationViewer {
    public static void main(String[] args) throws ClassNotFoundException {
        Class<?> clazz = Class.forName("java.util.ArrayList");
        System.out.println("类的标准名称:" + clazz.getCanonicalName());
        System.out.println("类的修饰符:" + Modifier.toString(clazz.getModifiers()));
        TypeVariable<?>[] typeVariables = clazz.getTypeParameters();
        System.out.println("类的泛型参数:");
        if (typeVariables.length != 0) {
            for (TypeVariable<?> typeVariable : typeVariables) {
                System.out.println(typeVariable + "\t");
            }
        } else {
            System.out.println("空");
        }
        Type[] interfaces = clazz.getGenericInterfaces();
        System.out.println("类所实现的接口");
        if (interfaces.length != 0) {
            for (Type type: interfaces) {
                System.out.println("\t" + type);
            }
        } else {
            System.out.println("\t" + "空");
        }
        Type superClass = clazz.getGenericSuperclass();
        System.out.println("类的直接继承类:");
        if (superClass != null) {
            System.out.println(superClass);
        } else {
            System.out.println("空");
        }
        Annotation[] annotations = clazz.getAnnotations();
        System.out.println("类的注解:");
        if (annotations.length != 0) {
            for (Annotation annotation: annotations) {
                System.out.println("\t" + annotation);
            }
        } else {
            System.out.println("空");
        }
    }
}

查看类的成员

// 查看类的成员
public class ClassViewer {
    @SuppressWarnings("unchecked")
    public static void main(String[] args) throws ClassNotFoundException {
        Class<?> clazz = Class.forName("java.util.ArrayList");
        System.out.println("类的标准名称:" + clazz.getCanonicalName());
        Constructor[] constructors = clazz.getConstructors();
        System.out.println("类的构造方法:");
        if (constructors.length != 0) {
            for (Constructor constructor: constructors) {
                System.out.println("\t" + constructor);
            }
        } else {
            System.out.println("\t 空");
        }
        Field[] fields = clazz.getFields();
        System.out.println("类的非继承域变量:");
        if (fields.length != 0) {
            for (Field field: fields) {
                System.out.println("\t" + field);
            }
        } else {
            System.out.println("\t 空");
        }
        Method[] methods = clazz.getMethods();
        System.out.println("类的非继承方法:");
        if (methods.length != 0) {
            for (Method method: methods) {
                System.out.println(method);
            }
        } else {
            System.out.println("\t 空");
        }
    }
}