中软国际Java EE全栈应用开发实训记录-Day2

本文详细介绍了Java中的泛型,包括通配符的基本使用、受限泛型以及类型擦除的概念和示例。同时,文章讲解了Java反射机制,阐述了反射在运行时动态获取类信息的能力,列举了获取Class、Constructor、Method和Field的方法,并提供了相关使用案例。
摘要由CSDN通过智能技术生成

一、泛型-续

1.泛型通配符

1.1 通配符基本使用

1.读取List<?>的对象list中的元素时,永远是安全的,因为不管list的真实类型是 什么,它包含的都是Object。
2.写入list中的元素时,不行。因为我们不知道的元素类型,我们不能向其中添加对象。
3.唯一的例外是null,它是所有类型的成员。调用get()方法并使用其返回值。返回值是一个未知的类型,但是我们知道,它总是一个Object

1.2 通配符例子的基础练习

package com.*;

import java.util.ArrayList; 
import java.util.List;

public class Test1 {
    public static void main(String[] args) {
     //        ?表示未知类型,允许在new类型时给一个确定的类型
        List<?> list = new ArrayList<String>();
        List<?> list1 = new ArrayList<Integer>(); 
        //        添加数据,因为?表示未知类型,添加具体数据的时候不允许添加
         //        如果允许添加具体数据,那么字符串添加“ABC”是否允许
          //报错
       //        如果允许,list集合类型是Integer还是String
      //       如果不允许添加“ABC”,那么为什么能添加3,就没有道理了
      //        list.add(3);
       //       唯一能添加的是null,因为null可以赋值给任何类型
        list.add(null);


//        获取元素
 //        增强for循环
        for(int i = 0;i <list1.size();i++){ 
        //            获取list<?>是允许的,因为不管是什么类型,肯定是Object的子类
            Object o = list1.get(i);
        }


        List<String> list2 = new ArrayList<String>();
        list2.add("aaa");
        for (int i = 0; i < list2.size() ; i++) {
            System.out.println(list2.get(i));
        }
    } 
 }
     ```

1.3 通配符高级使用-受限泛型

1.3.1 受限泛型的羽语法

可以指定泛型的上限和下限

//        受限泛型         泛型的上限         只接受该类型及其子类
        List<? extends String> list3 = new ArrayList<String>();

//        泛型的下限        只接受该类型及其父类,直至Object
        List<? super String> list4 = new ArrayList<String>(); 
1.3.2 例子练习
1.3.2.1 受限泛型的综合例子练习
java package com.*;

import java.util.ArrayList;
import java.util.List;

public class Test2 {
    public static void main(String[] args) {
        List<Number> list1 = new ArrayList<>();
        List<Integer> list2 = new ArrayList<>();
        List<String> list3 = new ArrayList<>();
        List<Object> list4 = new ArrayList<>();

        getElement1(list1);//允许的
        getElement1(list2);//允许的,因为Integer是Number的子类 
       //        不允许,编译报错,因为String不是Number的子类
       //        getElement1(list3); 
      //       编译报错,因为Object也不是Number的子类 
      //        getElement1(list4);

		//        允许的
        getElement2(list1);
         //        不允许的,编译报错,因为Integer不是Number的父类
         //  getElement2(list2);

//        编译报错,因为String不是Number的父类 
//        getElement2(list3); 
//        编译成功,因为Object是Number的父类
          getElement2(list4);
    }

//    lists的泛型类型是Number或Number的子类或者子孙类,泛型的上限
    public static void getElement1(List<? extends Number> lists){

    }

//    list的泛型类型是Number或Number的父类,直至Object,泛型的下限
    public static void getElement2(List<? super Number> lists){

    } 
}

1.3.2.1 泛型上限的例子练习
java package com.*;

public class Person<T> {
 //    定义成员变量
    private T val;

    public T getVal() {
        return val;
    }

    public void setVal(T val) {
        this.val = val;
    } 
    //    Person<? extends Number>的泛型的类型是Number或者Number的子孙类
    public static void show1(Person<? extends Number> p){
        System.out.println(p);
    }
    
  //    无意义,只能放String,没有String的子类
  //    Final虽然是String的子类,但不能被继承 //    语法规则正确,但是无意义
    public static void show3(Person<? extends String> p){
        System.out.println(p);
    }

//    Person<? super String>的泛型的类型是String或者String的父类
    public static void show2(Person<? super String> p){
        System.out.println(p);
    }
    //测试
    public static void main(String[] args) {
        Person<Integer> p1 = new Person<>();
        p1.setVal(1);
        Person<Double> p2 = new Person<>();
        p2.setVal(3.14);
        Person<String> p3 = new Person<>();
        p3.setVal("007");
        Person.show1(p1);
        //        编译报错,p1的泛型类型是Integer,不是String的父类 
        //        Person.show2(p1);
        Person.show1(p2);
        //        编译报错,p2的泛型类型是Double,不是String的父类 
        //        Person.show2(p2); 
        //        编译报错,p3的泛型类型是String,不是Number的子孙类
         //       Person.show1(p3);
        Person.show2(p3);

        Person<Object> p4 = new Person<>();
        p4.setVal(new Object());
        //        编译报错,p4的泛型类型是Object,不是Number的子孙类 
        //        Person.show1(p4); 
        //        允许,p4的泛型类型是Object,是String的父类
        Person.show2(p4);
    } 
 } 

2.类型擦除

2.1 类型擦除概念

泛型是java1.5版本才引进的概念,在这之前没有泛型,但是,泛型代码能够很好的和之前版本的代码兼 容,原因是泛型只存在代码编译阶段,在进入JVM 之前,泛型相关的信息会被擦除掉,我们称之为–类型擦除。
原始类型就是擦除去了泛型信息,最后在字节码中的类型变量的真正类型,无论何时定义一个泛型,相 应的原始类型都会被自动提供,类型变量擦除,并使用其限定类型(无限定的变量用Object)替换。

2.2 类型擦除例子练习

2.2.1 类型擦除例子基础练习一
package com.*;

import java.util.ArrayList; 
import java.util.List;

public class Test3 {
    public static void main(String[] args) {
        List<Integer> list1 = new ArrayList<>();
        List<String> list2 = new ArrayList<>(); 
        //        true,说明list1与list2的类型一致,说明Integer与String消失了
        System.out.println(list1.getClass() == list2.getClass());
    } 
}
2.2.2 类型擦除例子基础练习二
package com.*;

import java.util.List;

public class Erasure { 
//    参数列表类型或数量不同可以实现重载 
//    List<Integer> a和List<String> a会在成功编译之后擦除掉泛型类型,转换为基本类型Object,所以两个方法的参数列表类型是相同的,不能构成重载
//    public void test(List<Integer> a){ // //    } 
//    public void test(List<String> a){ // //    }

    public static void main(String[] args) {
        /*
        GT<Integer> gt1 = new GT<>(); 
        //        为gt1.var赋值为1
        gt1.var = 1;
        GT<String> gt2 = new GT<>();
        System.out.println(gt1.var);
        System.out.println("------------");
        gt2.var = 2; 
        //        gt1和gt2共享val值
        System.out.println("gt2.val:"+gt2.var);
        System.out.println("get1.val:"+gt1.var);
         */
    }

} 
class GT<E>{ //    静态变量
    public static int var = 8; //    方法   TODO 
}
对应运行结果:
1
------------
gt2.val:2 
get1.val:2

2.3 类型擦除特征

1.所有泛型类的类型参数在编译时都会被擦除,虚拟机运行时中没有泛型,只有普通类和普通方法, 从这一点上来说,Java中的泛型从某种程度上是一种语法糖。
2.Java泛型不支持基本类型 例如: short int double等。

3.在泛型代码内部,无法获得任何有关泛型参数类型的信息,如果传入的类型参数为T,那么在泛型
4.代码内部你不知道T有什么方法,属性,关于T的一切信息都丢失了。

5.创建泛型对象时请指明类型,让编译器尽早的做参数检查。

6.不要忽略编译器的警告信息,那意味着潜在的ClassCastException等着你。

7.Java的泛型类型不能用于new构建对象(也不能用于初始化数组)。泛型不能用于显性地引用运行时类型的操作之中,例如转型,instanceof和new操作(包括new一个对象,new一个数组),因为所有关于参数的类型信息都在运行时丢失了,所以任何在运行时需要获取类型信息的操作都无法进行工作。

二、反射

2.1 反射的概念

JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取信息以及动态调用对象的方法的功能称为java语言的反射机制。

2.2 反射的作用

动态获取类的信息,进一步实现需要的功能。

2.3 反射的相关的类

1.Class 类型
2.Constructor 构造方法
3.Method 方法
4.Field 属性
除了Class外,其他类都位于java.lang.reflect包中
可见,反射API将类的类型、方法、属性都封装成了类
其中最重要的类是Class,可以说,反射的使用都是从Class开始

2.3 反射的使用案例

2.3.1 获取Class

2.3.1.1 方法

在这里插入图片描述

2.3.1.2 案例
package com.*.fanxing;

public class TestPersonClass {
    public static void main(String[] args) throws ClassNotFoundException { 
    //        获取class对象的方式一 
    //        创建Person对象
        Person p = new Person();
        Class<? extends Person> cla1 =  p.getClass();
        System.out.println(cla1); 

   //        方式二 getsuperclass
        Class superClass = cla1.getSuperclass();
        System.out.println(superClass);

  //        方式三Class.forname
        Class cla2 = Class.forName("com.*.fanxing.Person");
        System.out.println(cla2);

  //        代码能编译通过,但会运行报错,因为没有AA这个类 
  //        String ClassName ="com.*.fanxing.AA"; 
  //        Class clas3 =Class.forName(ClassName); 
//        System.out.println(clas3);

//        方式四通过类名.class
        Class cla =  Person.class;
        System.out.println(cla);
        Class cla4 = Integer.class;
        System.out.println(cla4);
        Class cla5 = String.class;
        System.out.println(cla5);
    } 
}

2.3.2 获取Class的基本信息

2.3.2.1 方法

在这里插入图片描述

2.3.2.2 案例
java package com.*.fanxing;

import java.lang.reflect.Constructor; 
import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class TestPerson {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException,
IllegalAccessException, InstantiationException { 
//        获取class对象
        Class cla = Class.forName("com.ysu.fanxing.Person");
        System.out.println(cla); 
        //        获取所有public的属性
        System.out.println("获取所有public的属性");
        Field[] fields = cla.getFields();
        for (Field field:fields) {
            System.out.println(field);
        } 
        //        获取所有定义的属性
        System.out.println("获取所有定义的属性");
        Field[] fielde = cla.getDeclaredFields();
        for (Field field:fielde) {
            System.out.println(field);
        } 
        //        获取指定属性
        System.out.println("根据属性名称获取指定属性"); 
        //        getDeclaredField获取定义指定的属性(根据属性名称)
        Field f = cla.getDeclaredField("name");
        System.out.println(f);

//        获取共有方法
        System.out.println("获取所有的public方法,包含父类,父类的父类等等的方法");
        Method[] m = cla.getMethods();
        for(Method me:m){
            System.out.println(me);
        }

//        获取所有定义的方法
        System.out.println("获取所有定义的方法,类本身定义的方法");
        Method[] mr = cla.getDeclaredMethods();
        for(Method me:mr){
            System.out.println(me);
        }

//        获取指定方法
        System.out.println("根据方法名称和参数列表类型获取指定方法");
        Method mt = cla.getDeclaredMethod("abc", String.class,Integer.class);
        System.out.println(mt);
        Method mb = cla.getDeclaredMethod("abc", String.class);
        System.out.println(mb);

//        获取所有的构造方法
        System.out.println("获取所有的构造方法");
        Constructor[] con = cla.getConstructors();
        for(Constructor c:con){
            System.out.println(c);
        }

//        获取指定的构造方法
        System.out.println("获取指定的定义的构造方法");
        Constructor c = cla.getConstructor(String.class,Integer.class);
        System.out.println(c);

//        动态创建对象
        System.out.println("动态创建Person对象"); 
        //        编译期间不依赖Person,如果没有定义Person,编译能通过 
        //        调用Person无参构造方法
        Object o = cla.newInstance();
        System.out.println(o); 
        //        静态创建Person对象,编译时依赖定义Person,如果没有定义Person,编译不能通过 			
		//        Person pe =new Person();


        System.out.println("获取类的包名");
        Package pa = cla.getPackage();
        System.out.println(pa);

    } 
 }

2.4 反射的详解

2.4.1 Constructor类的使用

2.4.1.1 使用实例
package com.*.fanxing;

import java.lang.reflect.Constructor; 
import java.lang.reflect.InvocationTargetException;

public class PersonConstructor {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException,
InvocationTargetException, InstantiationException,
IllegalAccessException {

        String classname = "com.*.fanxing.Person"; 
        //        test1();
        test2(classname);
    }
    private static void test1() throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException,
InvocationTargetException, InstantiationException {

//        获取Person的Class对象
        Class<?> cla = Class.forName("com.*.fanxing.Person"); 
        //        通过Class对象获取指定的Constructor的构造方法
        Constructor<?> cl = cla.getDeclaredConstructor(String.class,Integer.class); 
        //       调用Constructor的newInstance方法调用Person的构造方法动态的创建Person的实例对象 
        //        传实参
//        o指的是Person对象
        Object o = cl.newInstance("cla",18); 
        //        打印Person对象
        System.out.println(o);
    }
    private static void test2(String classname) throws ClassNotFoundException, IllegalAccessException,
InvocationTargetException, InstantiationException { 
//       根据ClassName获取Class对象
        Class<?> cla = Class.forName(classname); 
        //        获取定义的所有构造方法
        Constructor<?>[] con = cla.getDeclaredConstructors();

//        遍历所有的构造方法,处理每一个构造方法
        for(Constructor<?> c:con){ 
        //            判断con有几个参数,不同的参数数量处理逻辑不同 
        //            获取构造方法参数的数量
            int count = c.getParameterCount();
            switch (count){
                case 0:{
                    Object o = c.newInstance();
                    System.out.println("使用无参的构造方法构建对象");
                    System.out.println(o);
                    break;
                }
                case 1:{
                    Class<?>[] pa = c.getParameterTypes();
                    if(pa[0].equals(String.class)){
                        Object o = c.newInstance("jack");
                        System.out.println("使用1个参数的构造方法构建对象");
                        System.out.println(o);
                    }else if(pa[0].equals(Integer.class)){
                        Object o = c.newInstance(10);
                        System.out.println("使用1个参数的构造方法构建对象");
                        System.out.println(o);
                    }
                    break;
                }
                case 2:{ 
                //                    获取构造方法的参数类型列表
                    Class<?>[] pa = c.getParameterTypes();
                    if(pa[0].equals(String.class)&&pa[1].equals(Integer.class)){
                        Object o = c.newInstance("jack",18);
                        System.out.println("使用2个参数(第一个参数是String)的构造方法构建对象");
                        System.out.println(o);
                    }else if(pa[0].equals(Integer.class)&&pa[1].equals(String.class)){
                        Object o = c.newInstance(10,"lock");
                        System.out.println("使用2个参数(第一个参数是Integer)的构造方法构建对象");
                        System.out.println(o);
                    }
                    break;
                }
                default:{
                    System.out.println("构造方法超过2个以上,未有处理逻辑");
                }
            }
        }
    } 
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值