反射 枚举 泛型 (java.lang)

目录

反射是什么

反射的用法

反射的优点和缺点

什么是枚举

解决了哪些问题

如何使用枚举

泛型是什么

为什么要使用泛型

泛型有哪些

怎么用泛型 


反射是什么

反射就是在程序运行过程中,动态的从类的字节码文件(class文件)中获取类或者对象中的属性。

反射的用法

三种反射方式获得实例对象

public class ReflectDemo {
    public static void main(String[] args) throws ClassNotFoundException {
        // Class.forName("");
        Class c1 = Class.forName("com.api.reflect.User");
        System.out.println(c1);
        // 类名.class
        Class c2 = User.class;
        System.out.println(c2);
        // .getClass
        User user = new User();
        Class c3 = user.getClass();
        System.out.println(c3);

        System.out.println(c1 == c2);
        System.out.println(c1 == c3);
        System.out.println(c2 == c3);
    }
}

获取成员变量

public Field getField(String name):获取指定名称的成员变量(public)。
public Field[]getFields():获取全部成员变量(public)。
public Field getDeclaredField(String name):不考虑修饰符。
public Field[]getDeclaredFields():不考虑修饰符。

public class User implements Serializable {
    public String name;
    protected Integer age;
    Integer sex;
    private String phone;

    public User() {
    }

    public User(String name, Integer age, Integer sex, String phone) {
        this.name = name;
        this.age = age;
        this.sex = sex;
        this.phone = phone;
    }

    private User(String name, Integer age) {
        this.name = name;
        this.age = age;
    }
    .........省略set、get

    public void setPhone(String phone) {
        this.phone = phone;
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", sex=" + sex +
                ", phone='" + phone + '\'' +
                '}';
    }

    public void run() {
        System.out.println("跑步...");
    }

    private void eat(String username) {
        System.out.println(username + "正在吃饭...");
    }
}
    上面的user对象编译成class文件,下面程序用反射机制获取成员变量;

public static void main(String[]args)throws Exception{
        // 1,通过Class.forName方式获取User对象
        Class aClass=Class.forName("com.api.reflect.User");
        // 获取public修饰的成员变量
        Field[]fields=aClass.getFields();
        for(Field field:fields){
        System.out.println("1,public修饰的成员变量--->"+field);
        }

        // 2,获取指定成员变量名称
        Field name=aClass.getField("name");
        System.out.println("2,指定成员变量名称--->"+name);

        // 3,获取全部的成员变量,忽略修饰符
        Field[]declaredFields=aClass.getDeclaredFields();
        for(Field declaredField:declaredFields){
        System.out.println("3,不考虑修饰符,获取全部成员变量--->"+declaredField);
        }
        // 4,获取指定成员变量
        Field phone=aClass.getDeclaredField("phone");
        // 获取访问权限,暴力反射
        phone.setAccessible(true);
        System.out.println("4,暴力反射--->"+phone);
        }

构造方法

public Constructor getConstructor(Class...parameterTypes)
public Constructor[]getConstructors()
public Constructor getDeclaredConstructor(Class...parameterTypes)
public Constructor[]getDeclaredConstructors()

public static void main(String[]args)throws Exception{
        // 通过Class.forName方式获取User对象
        Class aClass=Class.forName("com.api.reflect.User");
        // 1,获取指定构造方法(public),参数是可变参数
        Constructor constructor=aClass.getConstructor(String.class,Integer.class,Integer.class,String.class);
        // 实例化对象
        Object user=constructor.newInstance("张三",20,1,"123456");
        System.out.println(user);

        // 2,获取全部构造方法(public)
        Constructor[]constructors=aClass.getConstructors();
        for(Constructor constructor1:constructors){
        System.out.println(constructor1);
        }

        // 3,不考虑修饰符,获取指定构造方法
        Constructor declaredConstructor=aClass.getDeclaredConstructor(String.class,Integer.class);
        // 获取权限
        declaredConstructor.setAccessible(true);
        Object declaredUser=declaredConstructor.newInstance("李四",21);
        System.out.println("不考虑修饰符--->"+declaredUser);

        // 4,获取全部构造方法
        Constructor[]declaredConstructors=aClass.getDeclaredConstructors();
        for(Constructor declaredConstructor1:declaredConstructors){
        System.out.println("不考虑修饰符--->"+declaredConstructor1);
        }
        }

成员方法:

public Method getMethod(String name,Class...parameterTypes)
public Method[]getMethods()
public Method getDeclaredMethod(String name,Class...parameterTypes)
public Method[]getDeclaredMethods()

public static void main(String[]args)throws Exception{
        // 通过Class.forName方式获取User对象
        Class aClass=Class.forName("com.api.reflect.User");
        // 1,获取指定成员方法,public修饰
        Method methodRun=aClass.getMethod("run");
        // 实例化User,并调用invoke()执行方法
        Object user=aClass.newInstance();
        methodRun.invoke(user);
        // 2,获取全部成员方法,public修饰
        Method[]methods=aClass.getMethods();
        for(Method method:methods){
        System.out.println(method);
        }
        // 3,获取私有成员方法
        Method eat=aClass.getDeclaredMethod("eat",String.class);
        // 获取权限
        eat.setAccessible(true);
        // 执行方法
        eat.invoke(user,"小李");
        }

反射的优点和缺点

优点:1、可以在程序运行的过程中,动态的操作这些对象。2、可以解耦,提高程序的可扩展性。

缺点:1、因为是JVM操作,所以对于性能来说会有所下降。2、容易对程序源码造成一定的混乱。

什么是枚举

Enum关键字在java5开始引入,表示一种特殊类型的类,继承自java.lang.Enum类。被enum关键字修饰的类型就是枚举类型,提供了一些基本方法, 如果枚举值不添加任何方法,默认值从0开始。例如:

enum Color { RED, GREEN, BLUE } values();

返回enum实例的数组,而且该数组中的元素严格保持在enum中声明时的顺序。

name():返回实例名。

ordinal():返回实例声明时的次序,从0开始。

getDeclaringClass():返回实例所属的enum类型。

equals():判断是否为同一个对象。

可以使用 ==来比较enum实例。

此外,java.lang.Enum实现了Comparable和Serializable接口,所以也提供compareTo()方法。枚举 - C++中文 - API参考文档

解决了哪些问题

枚举和常量对比,允许进行编译时检查,预先记录可接受值的列表,并避免由于传入无效值而引起的意外行为。

如何使用枚举

举例:短信验证码有几种不同的用途,定义:

 publicenum PinType{
        REGISTER(100000, "注册使用"),
        FORGET_PASSWORD(100001, "忘记密码使用"),
        UPDATE_PHONE_NUMBER(100002, "更新手机号码使用");

        privatefinalint code;
        privatefinal String message;

        PinType( int code, String message){
             this.code = code;
             this.message = message;
        }
        public int getCode () {
             return code;
        }
        public String getMessage () {
             return message;
        }
        @Override
        public String toString () {
             return "PinType{" +
                "code=" + code +
                ", message='" + message + '\'' +
                '}';
        }
    }

实际使用:

System.out.println(PinType.FORGET_PASSWORD.getCode());
System.out.println(PinType.FORGET_PASSWORD.getMessage());
System.out.println(PinType.FORGET_PASSWORD.toString());

Output:
100001
忘记密码使用
PinType{code=100001, message='忘记密码使用'}

错误码枚举类型的定义

public enum ErrorCodeEn {
    OK(0, "成功"),
    ERROR_A(100, "错误A"),
    ERROR_B(200, "错误B");

    ErrorCodeEn(int number, String description) {
        this.code = number;
        this.description = description;
    }

    private int code;
    private String description;

    public int getCode() {
        return code;
    }

    public String getDescription() {
        return description;
    }

    public static void main(String args[]) {
        for (ErrorCodeEn s : ErrorCodeEn.values()) {
            System.out.println("code: " + s.getCode() + ", description: " + s.getDescription());
        }
    }
}

枚举可以实现接口

通过实现接口,可以约束它的方法。

public interface INumberEnum {
    int getCode();
    String getDescription();
}
public enum ErrorCodeEn2 implements INumberEnum {
    OK(0, "成功"),
    ERROR_A(100, "错误A"),
    ERROR_B(200, "错误B");

    ErrorCodeEn2(int number, String description) {
        this.code = number;
        this.description = description;
    }

    private int code;
    private String description;

    @Override
    public int getCode() {
        return code;
    }

    @Override
    public String getDescription() {
        return description;
    }
}

通过枚举实现一些设计模式

策略枚举:

enum PayrollDay {
    MONDAY(PayType.WEEKDAY), TUESDAY(PayType.WEEKDAY), WEDNESDAY(
            PayType.WEEKDAY), THURSDAY(PayType.WEEKDAY), FRIDAY(PayType.WEEKDAY), SATURDAY(
            PayType.WEEKEND), SUNDAY(PayType.WEEKEND);

    private final PayType payType;
    PayrollDay(PayType payType) {
        this.payType = payType;
    }
    double pay(double hoursWorked, double payRate) {
        return payType.pay(hoursWorked, payRate);
    }
    private enum PayType {
        WEEKDAY {
            double overtimePay(double hours, double payRate) {
                return hours <= HOURS_PER_SHIFT ? 0 : (hours - HOURS_PER_SHIFT)
                        * payRate / 2;
            }
        },
        WEEKEND {
            double overtimePay(double hours, double payRate) {
                return hours * payRate / 2;
            }
        };
        private static final int HOURS_PER_SHIFT = 8;

        abstract double overtimePay(double hrs, double payRate);

        double pay(double hoursWorked, double payRate) {
            double basePay = hoursWorked * payRate;
            return basePay + overtimePay(hoursWorked, payRate);
        }
    }
}

测试:

System.out.println("时薪100的人在周五工作8小时的收入" + PayrollDay.FRIDAY.pay(8.0, 100));
System.out.println("时薪100的人在周六工作8小时的收入" + PayrollDay.SATURDAY.pay(8.0, 100));

泛型是什么

Java泛型(generics)是JDK5开始引入的一个新特性,指所操作的数据类型被指定为一个参数。

为什么要使用泛型

在没有泛型的情况的下,通过对类型Object的引用来实现参数的"任意化","任意化"带来的缺点是要做显式的强制类型转换,而这种转换是要求开发者对实际参数类型可以预知的情况下进行的。对于强制类型转换错误的情况,编译器可能不提示错误,在运行的时候才出现异常,这本身就是一个安全隐患。那么泛型的好处就是在编译的时候能够检查类型安全,并且所有的强制转换都是自动和隐式的。

泛型有哪些

? 表示不确定的java类型
T (type) 表示具体的一个java类型
K V (key value) 分别代表java键值中的Key Value
E (element) 代表Element

怎么用泛型 

?无界通配符

有一个父类Animal和N个子类:
static int countLegs (List animals ) {
        int retVal = 0;
        for (Animal animal : animals){
        retVal += animal.countLegs();
        }
        return retVal;
        }

static int countLegs1 (List animals){
        int retVal = 0;
        for (Animal animal : animals ){
        retVal += animal.countLegs();
        }
        return retVal;
        }

public static void main(String[] args) {
        List dogs = new ArrayList<>();
        // 不会报错
        countLegs( dogs );
        // 报错
        countLegs1(dogs);
        }

对于不确定要操作的类型,可以使用无限制通配符(即 ),表示可以持有任何类型。像countLegs方法中,限定了上界,但是不关心具体类型是什么,所以对于传入的Animal的所有子类都可以支持,而countLegs1 就不行。

上界通配符 < ? extends E>

上届:用extends关键字声明,表示参数化的类型可能是所指定的类型,或者是此类型的子类。在类型参数中使用extends表示这个泛型中的参数必须是E或者E的子类,这样有两个好处:

如果传入的类型不是E 或者 E 的子类,编译不成功
private  E test(K arg1, E arg2){
        E result = arg2;
        arg2.compareTo(arg1);
        //.....
        return result;
        }
类型参数列表中如果有多个类型参数上限,用逗号分开

下界通配符 < ? super E>

下界: 用super进行声明,表示参数化的类型可能是所指定的类型,或者是此类型的父类型

private  void test(List dst, List src){
        for (T t : src) {
        dst.add(t);
        }
        }
public static void main(String[] args) {
        List dogs = new ArrayList<>();
        List animals = new ArrayList<>();
        new Test3().test(animals,dogs);
        }
// Dog 是 Animal 的子类
class Dog extends Animal {

}

dst类型 “大于等于” src 的类型,这里的“大于等于”是指 dst 表示的范围比 src 要大,因此装得下 dst 的容器也就能装 src 。

?和 T 的区别

T 是一个确定的类型,通常用于泛型类和泛型方法的定义,?是一个 不确定 的类型,通常用于泛型方法的调用代码和形参,不能用于定义类和泛型方法。比如如下这种 :

区别1:通过T来确保泛型参数的一致性

public  void test(List dest, List src)
//通配符是不确定的,所以这个方法不能保证两个List具有相同的元素类型
public void test(List dest, List src)

区别2:类型参数可以多重限定而通配符不行

public class Multilimit implements MultilimitInterfaceA,MultilimitInterfaceB{
    //使用&符号设定多重边界
    public static void test(T t){
    }
}

interface MultilimitInterfaceA{}
interface MultilimitInterfaceB{}

 区别3:通配符可以使用超类限定而类型参数不行 类型参数 T只具有一种类型限定方式:

T extends A

但是通配符 ? 可以进行 两种限定:

? extends A
? super A

Class和 Class区别

常见的是在反射场景下的使用

// 通过反射的方式生成multiLimit对象
MultiLimit multiLimit = (MultiLimit)
                    Class.forName("com.glmapper.bridge.boot.generic.MultiLimit").newInstance();
对于上述代码,在运行期,如果反射的类型不是MultiLimit类,那么一定会报 
ava.lang.ClassCastException错误。

Class在实例化的时候,T 要替换成具体类。Class它是个通配泛型,? 可以代表任何类型,
所以主要用于声明时的限制情况。

你可能感兴趣的:(Java基础,java,后端)