zoukankan      html  css  js  c++  java
  • Java高级应用之泛型与反射

    /******************************************************************************************************************/

    一、泛型

    不同的数据结构可以用同样的操作就是泛型

    1.类使用泛型

    class Person<T> {//要使用泛型的类加上类似与通配符的<T>,中间字符可以任意

    //也可以传入多个类型<T,N>

        private T age;//内部类型,使用T代替

        public void setAge(T age) {//内部类型,使用T代替

            this.age = age;

        }

        public T getAge() {//内部类型,使用T代替

            return this.age;

        }

    }

    public class Generics {

        public static void main(String args[]) {

            Person<String> p = new Person<String>();//创建对象的时候传入类型,如果是两个则<>里面放两个类型

            p.setAge("3 years old");

            //System.out.println(p.getAge());

            printInfo(p);

            Person<Integer> p2 = new Person<Integer>();//<>内只能用类,所以不能用基础数据类型int要用Integer类代替

            p2.setAge(3);

            //System.out.println(p2.getAge());

            printInfo(p2);

            Person<?> p3;

            p3 = p;//通用引用p3赋值

            //p3.setAge("4 years");//不能设置(类型冲突)

            p3.getAge();//但可以获取

        }

        public static void printInfo(Person<?> p) {//Person<?>是通配符,表示传进来可以是String类也可以是Integer类

            System.out.println(p.getAge());

        }

    }

    2.方法使用泛型

    1)定义

    public static <T> void printInfo2(Person<T> p) {//定义格式返回值前面,参数里都要有相应的<T>

        System.out.println(p.getAge());

    }

    2)调用

    printInfo2(p);//p为具体的对象Person<String> p = new Person<String>();

    printInfo2(p2);

    printInfo2(p3);

    3.子类使用泛型(泛型的继承)

    1)定义

    class Student<T> extends Person<T> {//子类继续使用泛型

    }

    class Student2 extends Person<String> {//子类不再使用泛型

    //子类确定类型为String,所以前面也就不需要加<T>,同时对应父类的类型也已被确定

    }

    2)调用

    Student<Integer> s = new Student<Integer>();//创建子类对象,传入类型

    s.setAge(10);//调用父类的方法

    printInfo(s);//向上转换

    Student2 s2 = new Student2();//已经确定类型,不需传入

    s2.setAge("11 years");//对应父类的类型已被确定,调用父类方法直接传入String

    printInfo(s2);

    4.接口使用泛型(接口:特殊的父类)

    interface Person<T> {

        public void setAge(T age);

        public T getAge();

    }

    class Student<T> implements Person<T> {//子类继续使用泛型

        T age;

        public void setAge(T age){

            this.age = age;

        }

        public T getAge() {

            return this.age;

        }

    }

    class Student2 implements Person<String> {//指定对应接口的泛型类型

        String age;///已经确定了类型,直接使用

        public void setAge(String age){

            this.age = age;

        }

        public String getAge() {

            return this.age;

        }

    }

    public static void main(String args[]) {

        Student<Integer> s = new Student<Integer>();//创建子类对象,传入类型

        s.setAge(10);

        printInfo(s);//向上转换(接口:特殊的父类),子类里实现了方法,同时父类也就是接口没有实现对应的方法,所以里面调用的是子类的方法

        Student2 s2 = new Student2();//已经确定类型,不需传入

        s2.setAge("11 years");

        printInfo(s2);

    }

    public static void printInfo(Person<?> p) {

        System.out.println(p.getAge());

    }

    5.受限泛型

    声明泛型的时候可以指定泛型的上限和下限

    1)泛型的上限:<T extends Number> T只能是Number类或其子类

    /*T只能是Number类或其子类Integer, Float 等*/

    class Student<T extends Number> implements Person<T> {

        T age;

        public void setAge(T age)

        {

            this.age = age;

        }

        public T getAge() {

            return this.age;

        }

    }

    2)泛型的下限:<? super String> T只能是String类或其父类

    super只能使用通配符,不能直接在名字里使用(即只能在使用这个泛型的时候再指定),所以还是:

    class Student<T> implements Person<T> {

        T age;

        public void setAge(T age)

        {

            this.age = age;

        }

        public T getAge() {

            return this.age;

        }

    }

    //使用的时候指定下限(用通配符?指定下限)

    public static void printInfo(Person<? super String> p) {//传进来 只能是String类或其父类   

        System.out.println(p.getAge());

    }

    调用:

    Student<String> s = new Student<String>();

    s.setAge("10");

    printInfo(s);

    /******************************************************************************************************************/

    二、反射

    正常步骤我们是import"包.类"然后通过new实例化最后得到实例化对象

    那么能否反过来呢

    从实例化对象得到getClass方法最后得到完整的"包.类"名称

    这个反过来的操作(根据实例化对象得到完整的"包.类"名称)就是所谓的反射操作(当然反射的作用不仅仅是得到完整的"包.类"名称)

    注意:在反射操作中,一切的操作都使用Object完成,类,数组的引用都可以使用object进行接收

    1.class对象和"类的实例化对象"

    Person p=new Person();

    System.out.println(p.getClass().getName());//打印"包.类"名称

    JVM会加载*.Class文件(java写的类)到内存里,也就是会在内存里创建一个class object用来描述这个类,包括类的包,类名称,构造方法,方法,属性,这样就可以使用这个class object来实例化对象(在内存里对于一个class只有一个class object,这个class object是用来描述类本身的,我们可以使用这个class object来创建实例化对象)可以有三种方法获得一个类的class object:

    2.获得class

    1).Class<?> c=Class.forName("包.类");

    Class<?> c1 = null;

    try {

        c1 = Class.forName("a.b.c.d.Person");

    } catch (ClassNotFoundException e) {

        System.out.println(e);

    }

    System.out.println(c1.getName());

    2).Class<?> c=new X().getClass();

    Person p = new Person();

    Class<?> c2 = p.getClass();

    System.out.println(c2.getName());

    3).Class<?> c=X.class

    Class<?> c3 = Person.class;

    System.out.println(c3.getName());

    3.对于数组或者其他数据类型,也有对应的类,对应的class object

    int arr[] = {1,2,3};

    int arr2[] = {1,2,3,4};

    int arr3[][] = {{1,2,3,4},{1}};

    Class<?> c4 = arr.getClass();

    Class<?> c5 = arr2.getClass();

    Class<?> c6 = arr3.getClass();

    Class<?> c7 = int.class;

    System.out.println(c4.getName());

    System.out.println(c5.getName());

    System.out.println(c6.getName());

    System.out.println(c7.getName());//基本的数据类型也是一个类,里面也有class

    System.out.println((c4 == c5));//同样的数组类型是一样的class

    System.out.println((c4 != c6));//二维数组和一维数组不一样所以不一样的class

    4.使用反射来获取类的实例化对象(类的属性与方法),取代import a.b.c.d.Person;(的形式)

    public static void main(String args[]) throws Exception {//Exception 是其他的异常父类,所以可以这样代替InstantiationException等其他异常

    Class<?> c = null;

    try {

        c = Class.forName("a.b.c.d.Person");//使用名字来获得这个Person的class object

    } catch (ClassNotFoundException e) {

        System.out.println(e);

    }

    Object p = null;//然后创建它的实例化对象(Object是所有类的父类,所以可以这么写)

    try {

        p = c.newInstance();//然后创建它的实例化对象(Object是所有类的父类,所以可以这么写(可以向上转换)),实际是调用了那个类的无参构造方法

    } catch (InstantiationException e) {

        System.out.println(e);

    }

    //调用有参构造方法的实例化

    Constructor<?> con = c.getConstructor(String.class);//获得参数是String的构造方法 的class

    Object p2 = con.newInstance("123");//然后创建它的实例化对象,有参数

    5.获得并调用类的方法:

    Method set = c.getMethod("setName", String.class);//传入方法名称(该方法在定义的地方要有public权限),参数类型

    set.invoke(p2, "123");//调用实例化对象中的方法,要设置的实例化对象,然后才是传入的值

    set.invoke(p, "abc");

    //对于静态方法, invoke的第1个参数可以写为null

    Method get = c.getMethod("getName");//没有传入参数

    System.out.println(get.invoke(p));

    System.out.println(get.invoke(p2));

    6.读取或设置类的属性

    1).最好是用上面的方式,通过方法来访问类的属性

    2).也可以直接获取属性:

    (1)Field f = c.getField(String name); // 获得公共属性, 此方法先搜本类, 再搜它实现的接口,最后在父类中搜索

    (2)Field name = c.getDeclaredField("name");//可以获得类里面所有属性(包括private,public等)中名为name的属性

    name.setAccessible(true);//设置为可访问,如果要访问的属性为public可以不要这句话,其实设置为可访问也就破坏了类的封装性,所以一般不使用这种方法,而是去调用那个类的设置获取方法

    name.set(p, "www");//设置某个实例化对象中的name属性

    name.set(p2, "123");//设置p2这个实例化对象中的name属性

    System.out.println(name.get(p));//获得某个对象的name属性

    System.out.println(name.get(p2));

    7.使用反射的好处

    增加程序灵活性,通过类的名称(放在文件里或者通过参数传递,这就不用在代码中写死,传入什么就可以实例化什么),然后就可以实例化出不同的对象

  • 相关阅读:
    快速认识ELK中的L
    HBase启动和停止命令
    Kafka 快速起步(作者:杜亦舒)
    Kafka 消息存储及检索(作者:杜亦舒)
    HBase集群搭建
    Kafka消息保证不丢失和重复消费问题
    Kafka文件的存储机制
    Kafka的配置文件详细描述
    kafka常用操作命令
    BZOJ1769 : [Ceoi2009]tri
  • 原文地址:https://www.cnblogs.com/jpfss/p/10879987.html
Copyright © 2011-2022 走看看