Java中泛型和反射机制

泛型

为什么要使用泛型

Java中,ArrayList默认接受Object类型的对象,所以所有对象都可以放进ArrayList中

所以get(0)返回的类型为Object,需要进行强制转换才能得到自己想要的数据类型

如果你的记忆力足够好,记得之前它是什么类型的话

但是开发人员总会犯错误的,有可能会将该对象转换为另外一个数据类型,难免出现类型转换异常

  • 使用泛型的好处:

    泛型的用法是在容器后面添加<Type>,Type可以是类、抽象类、接口

    泛型表示这种容器只能存放这一种数据类型,别的类型就放不进去。

    如果容器的泛型是两个子类的父类,则它的两个子类也可以放进这个容器之中。但与该父类无关的类型还是放不进去

    练习熟悉:用泛型实现一个集合,使之能够存放整数,也能存放浮点数,但是存不进去字符串。

通配符

? extends

ArrayList heroList<? extends Hero> 表示这是一个Hero泛型或者其子类泛型
heroList 的泛型可能是Hero
heroList 的泛型可能是APHero
heroList 的泛型可能是ADHero
所以 可以确凿的是,从heroList取出来的对象,一定是可以转型成Hero的

但是,不能往里面放东西,因为
放APHero就不满足<ADHero>
放ADHero又不满足<APHero>

? super

ArrayList heroList<? super Hero> 表示这是一个Hero泛型或者其父类泛型
heroList的泛型可能是Hero
heroList的泛型可能是Object

可以往里面插入Hero以及Hero的子类
但是取出来有风险,因为不确定取出来是Hero还是Object

总结

  • 如果希望只取出,不插入,就使用? extends Hero
  • 如果希望只插入,不取出,就使用? super Hero
  • 如果希望,又能插入,又能取出,就不要用通配符?

反射机制

类对象:

用于描述某种类都有什么属性,什么方法。

在一个JVM中,一种类只会有一个类对象存在。获取类对象的方式有以下三种:

  1. Class c1 = Class.forName(类名)
  2. Class c2 = 类名.class(如:Hero.class)
  3. Class c3 = new 类名().getClass()(如:new Hero().getClass() )

准确地说,是在一个类加载器ClassLoader下,一种类只会有一个类对象存在,而通常一个JVM下只会有一个ClassLoader。

利用反射机制创建一个对象

public class TestReflection {
 
   public static void main(String[] args) {
       //传统的使用new的方式创建对象
       Hero h1 =new Hero();
       h1.name = "teemo";
       System.out.println(h1);
         
       try {
           //使用反射的方式创建对象
           String className = "charactor.Hero";
           //类对象
           Class pClass=Class.forName(className);
           //构造器
           Constructor c= pClass.getConstructor();
           //通过构造器实例化
           Hero h2= (Hero) c.newInstance();
           h2.name="gareen";
           System.out.println(h2);
       } catch (Exception e) {
           // TODO Auto-generated catch block
           e.printStackTrace();
       }
   }
}

访问属性

通过反射修改属性的值

public class TestReflection {
  
    public static void main(String[] args) {
            Hero h =new Hero();
            //使用传统方式修改name的值为garen
            h.name = "garen";
            try {
                //获取类Hero的名字叫做name的字段
                Field f1= h.getClass().getDeclaredField("name");
                //修改这个字段的值
                f1.set(h, "teemo");
                //打印被修改后的值
                System.out.println(h.name);
                 
            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
    }
}

getField 和 getDeclaredField

这两个方法都是用于获取字段

  • getField 只能获取public的,包括从父类继承来的字段。

  • getDeclaredField 可以获取本类所有的字段,包括private的,但是不能获取继承来的字段。

    注: 这里只能获取到private的字段,但并不能访问该private字段的值,除非加上setAccessible(true)

通过反射机制,调用一个对象的方法

先实现一个Hero类并给它的name属性增加setName和getName方法

public class Hero {
    public String name;
    public float hp;
    public int damage;
    public int id;
     
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Hero(){
         
    }
    public Hero(String string) {
        name =string;
    }
    @Override
    public String toString() {
        return "Hero [name=" + name + "]";
    }
    public boolean isDead() {
        // TODO Auto-generated method stub
        return false;
    }
    public void attackHero(Hero h2) {
        // TODO Auto-generated method stub
    }
 
}

再通过反射机制调用Hero的setName

public class TestReflection {
    public static void main(String[] args) {
        Hero h = new Hero();
        try {
            // 获取这个名字叫做setName,参数类型是String的方法
            Method m = h.getClass().getMethod("setName", String.class);
            // 对h对象,调用这个方法
            m.invoke(h, "盖伦");
            // 使用传统的方式,调用getName方法
            System.out.println(h.getName());
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

为什么有反射机制(Spring框架的最基本原理)

为什么要用反射机制?感觉还不如用直接调用方法来的直接和方便

eg:在不同的两个类之间进行方法切换

  1. 先准备两个简单的业务类,各自都有一个业务方法,分别打印不同的字符串

    public class Service1{
        public void doServie1{
            System.out.println("业务方法1");
        }
    }
    
    public class Service2{
        public void doServie1{
            System.out.println("业务方法2");
        }
    }
    
  2. 利用非反射方式:

    需要从第一个业务方法切换到第二个业务方法时候,利用非反射方式,必须修改代码,并且重新编译运行,才能实现

    public class Test{
        public static void main(String[] args){
            new Service1().doService1();
        }
    }
    

    需要切换时,得做如下修改之后,进行编译

    public class Test{
        public static void main(String[] args){
            //new Service1().doService1();
            new Service2().doService2();
        }
    }
    
  3. 利用反射方式:

    1. 准备一个配置文件spring.txt,置于src目录下,文件中存放类的名称以及要使用的方法名。

      spring.txt文件内容:

      class=reflection.Service1
      method=doService1
      
    2. 在测试类reflectTest中,首先取出类名称和方法名,通过反射调用这个方法。

      Test.java

      
      public class Test {
          @SuppressWarnings({ "rawtypes", "unchecked" })
          public static void main(String[] args) throws Exception {
       
              //从spring.txt中获取类名称和方法名称
              File springConfigFile = new File("e:\\project\\j2se\\src\\spring.txt");
              Properties springConfig= new Properties();
              springConfig.load(new FileInputStream(springConfigFile));
              String className = (String) springConfig.get("class");
              String methodName = (String) springConfig.get("method");
            
              //根据类名称获取类对象
              Class clazz = Class.forName(className);
              //根据方法名称,获取方法对象
              Method m = clazz.getMethod(methodName);
              //获取构造器
              Constructor c = clazz.getConstructor();
              //根据构造器,实例化出对象
              Object service = c.newInstance();
              //调用对象的指定方法
              m.invoke(service);
          }
      }
      

    当需要从第一个业务方法切换到调用第二个业务方法时候,不需要修改代码,也不用重新编译,只需要修改配置方法spring.txt,再运行即可

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值