java是一种可以撰写跨平台应用软件的面向对象的程序设计语言,是由Sun Microsystems公司于1995年5月推出的Java程序设计语言和Java平台(即JavaEE, JavaME, JavaSE)的总称。本站提供基于Java框架struts,spring,hibernate等的桌面应用、web交互及移动终端的开发技巧与资料

保持永久学习的心态,将成就一个优秀的你,来 继续搞起java知识。

19 反射

19.1 类的加载概述和加载时机

19.1.1 类的加载概述

当程序要使用某个类时,如果该类还未加载到内存中,则系统会通过加载、连接、初始化三步来实现对这个类进行初始化。加载:

指将class文件读入内存中,并为之创建一个Class对象,任何类被使用时系统都会建立一个Class对象。

连接:

验证:是否有正确的内部结构,并和其他类协调一致

准备:负责为类的静态成员分配内存,并设置默认初始化值

解析:将类的二进制数据中的符号引用替换为直接引用

初始化:即初始化步骤。

19.1.2 加载时机

创建类的实例访问类的静态变量,或者为静态变量赋值

调用类的静态方法

使用反射方式来强制创建某个类或接口对应的java.lang.Class对象

初始化某个类的子类

直接使用java.exe命令来运行某个主类。

19.2 类加载器的概述和分类

19.2.1 类加载器的概述

负责将.class文件加载到内存中,并为之生成对应的Class对象。虽然我们并不关心类加载机制,但是了解这个机制可以更好理解程序运行。

19.2.2 类加载器的分类

Bootstrap ClassLoader 根类加载器

Extension ClassLoader 扩展类加载器

System ClassLoader 系统类加载器

19.2.3 类加载器的作用

Bootstrap ClassLoader 根类加载器 也称为引导类加载器,负责Java核心类的加载

比如System,String等,在JDK中JRE的lib目录下rt.jar文件中

Extension ClassLoader 扩展类加载器

负责JRE的扩展目录中jar包的加载

在JDK中JRE的lib目录下ext目录

System ClassLoader 系统类加载器

负责在JVM启动时加载来自java命令的class文件,以及classpath环境变量所指定的jar包和类路径

19.3 反射概述

概述 JAVA反射机制是运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;

对于任意一个对象,都能够调用他的任意一个方法和属性;

这种动态获取的信息以及动态调用的方法的功能称为java语言的反射机制;

想要解剖一个类,必须先要获取到该类的字节码文件对象;

而解剖使用的就是Class类中的方法,所以先要获取到每一个字节码文件对应Class类型的对象。

例1:获取字节码文件对象的三种方式

Person.java

1        package com.mirror.gf;

        public class Person {
            private String name;
            private int age;
            public Person(){
                super();
            }
            public Person(String name,int age){
                super();
                this.name = name;
                this.age = age;
            }

            public boolean equals(Object obj) {
                if (this == obj)
                    return true;
                if (obj == null)
                    return false;
                if (getClass() != obj.getClass())
                    return false;
                Person other = (Person) obj;
                if (age != other.age)
                    return false;
                if (name == null) {
                    if (other.name != null)
                        return false;
                } else if (!name.equals(other.name))
                    return false;
                return true;
            }                      
        }

Demo_Reflect.java

1package com.mirror.gf;

public class Demo_Reflect {
    public static void main(String[] args) throws ClassNotFoundException{
    Class clazz1 = Class.forName("com.mirror.gf.Person");
    Class clazz2 = Person.class;
    Person p = new Person();
    Class clazz3 = p.getClass();

    System.out.println(clazz1== clazz2);
    System.out.println(clazz2== clazz3);
    }
}

运行结果:

ture

ture

例2:Class.forName()读取配置文件举例Demo2_Reflect.java

1package com.mirror.gf;

import java.io.BufferedReader;
import java.io.FileReader;

public class Demo2_Reflect {

    /**
     * @param args
     * @throws Exception 
     */
    public static void main(String[] args) throws Exception {
        //使用反射与配置文件
        BufferedReader br = new BufferedReader(new FileReader("config.properties"));
        Class clazz = Class.forName(br.readLine());
        Fruit f = (Fruit) clazz.newInstance();

        Eat eat = new Eat();
        eat.run(f); 

        br.close(); 
        //未用反射
        /*Eat eat = new Eat();
        eat.run(new Orange());*/
    }
}
interface Fruit{
    public void eat();
}

class Apple implements Fruit{
    public void eat(){
    System.out.println("吃苹果");
    }
}

class Orange implements Fruit{
    public void eat(){
    System.out.println("吃橙子");
    }
}

class Eat{
    public void run(Fruit f){
        f.eat();
    }
}

配置文件congfig.properties:

com.mirror.gf.Apple

运行结果:吃苹果

例3:通过反射获取带参构造方法并使用 Class类的newInstance()方法时使用该类无参的构造函数,如果一个类没有无参的构造函数,就不能这样创建,可以调用Class类的getConstructor(String.class,int.class)方法获取一个指定的构造函数然后再调用Constructor类的newInstance(“张三”,20)方法创建对象

Ps:建立的项目需要jdk1.5以上才能正常使用getConstructor的方法

Person.java

1package com.mirror.gf;

public class Person {
    private String name;
    private int  age;
    public Person(){
        super();
    }
    public Person(String name,int age){
        super();
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String toString() {
        return "Person [name=" + name + ", age=" + age + "]";
    }

    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        Person other = (Person) obj;
        if (age != other.age)
            return false;
        if (name == null) {
            if (other.name != null)
                return false;
        } else if (!name.equals(other.name))
            return false;
        return true;
    }                      
}

Demo3_Constructor.java

1    package com.mirror.gf;

    import java.lang.reflect.Constructor;

    public class Demo3_Constructor {
        public static void main(String[] args) {
            try {
                Class clazz = Class.forName("com.mirror.gf.Person");
                //通过无参构造创建对象
                //Person p = (Person) clazz.newInstance();
                //获取有参构造
                Constructor c = clazz.getConstructor(String.class,int.class);
                //通过有参构造对象
                Person p = (Person) c.newInstance("Gabriel",27);
                System.out.print(p);
            } catch (Exception  e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            //通过无参构建对象
            /*  Person p = (Person) clazz.newInstance();
            System.out.print(p);*/
    //      Constructor c = clazz.getConstructor(String.class,int.class);
        }
    }

运行结果:Person [name=Gabriel, age=27]

例4:通过反射获取成员变量并使用Class.getField(String)方法获取类中的指定字段(可见的),如果是私有的可以用getDeclareField(“name”)方法获取,通过set(obj,”李四”)方法可以设置指定对象上该字段的值,如果是私有的需先调用setAccessible(true)设置访问权限,用获取的字段调用get(obj)可以获取指定对象中该字段的值

Demo3_Field.java

1package com.mirror.gf;

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

public class Demo4_Field {
    /**
     * Class.getField(String)方法获取类中的指定字段(可见的),如果是私有的可以用getDeclareField("name")
     * 方法获取,通过set(obj,"李四")方法可以设置指定对象上该字段的值,如果是私有的需先调用setAccessible(true)设置访问权限,
     * 用获取的字段调用get(obj)可以获取指定对象中该字段的值
     * @throws Exception 
     */
    public static void main(String[] args) throws Exception {
        Class clazz = Class.forName("com.mirror.gf.Person");
        Constructor c = clazz.getConstructor(String.class,int.class);
        //通过有参构造对象
        Person p = (Person) c.newInstance("Gabriel",27);
        //获取名字字段
        Field f = clazz.getDeclaredField("name");
        //去除私有权限
        f.setAccessible(true);
        //修改姓名
        f.set(p, "李四");
        System.out.print(p);
    }
}

例5:通过反射获取方法并使用

Class.getMethod(String,Class…)和Class.getDeclaredMethod(String,Class…)方法可以获取类中的指定方法,调用invoke(Object,Object…)可以调用该方法,Class.getMethod(“eat”)invoke(obj),Class.getMethod(“eat”,int.class)invoke(obj,10)

Person.java

1    package com.mirror.gf;

    public class Person {
        private String name;
        private int  age;
        public Person(){
            super();
        }
        public Person(String name,int age){
            super();
            this.name = name;
            this.age = age;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public String toString() {
            return "Person [name=" + name + ", age=" + age + "]";
        }

        public boolean equals(Object obj) {
            if (this == obj)
                return true;
            if (obj == null)
                return false;
            if (getClass() != obj.getClass())
                return false;
            Person other = (Person) obj;
            if (age != other.age)
                return false;
            if (name == null) {
                if (other.name != null)
                    return false;
            } else if (!name.equals(other.name))
                return false;
            return true;
        }       

        public void eat(){
            System.out.println("吃了午饭");
        }

        public void eat(int num){
            System.out.println("今天吃了"+num+"顿");
        }
    }

Demo5_Method.java

1package com.mirror.gf;

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

public class Demo5_Method {

    /**
     *Class.getMethod(String,Class...)和Class.getDeclaredMethod(String,Class...)方法可以获取类中的指定方法,
     *调用invoke(Object,Object...)可以调用该方法,
     *Class.getMethod("eat")invoke(obj),Class.getMethod("eat",int.class)invoke(obj,10)
     * @throws Exception 
     */
    public static void main(String[] args) throws Exception {
        Class clazz = Class.forName("com.mirror.gf.Person");
        Constructor c = clazz.getConstructor(String.class,int.class);
        Person p = (Person) c.newInstance("Gabriel",27);

        Method m = clazz.getMethod("eat");
        m.invoke(p, null);

        Method m1 = clazz.getMethod("eat", int.class);
        m1.invoke(p, 3);
    }
}

运行结果:

吃了午饭

今天吃了3顿

例6:通过反射越过泛型检查 泛型只在编译期有效,在运行期会被擦除掉;故可通过反射拿到编译后的字节码文件进行操作,从而越过泛型进行操作

1package com.mirror.gf;

import java.lang.reflect.Method;
import java.util.ArrayList;

public class Test1 {

    /**
     *泛型只在编译期有效,在运行期会被擦除掉
     * @throws Exception 
     */
    public static void main(String[] args) throws Exception {
        ArrayList<Integer> list = new ArrayList();
        list.add(1);
        list.add(2);

        Class clazz = Class.forName("java.util.ArrayList");
        Method m = clazz.getMethod("add",Object.class);
        m.invoke(list, "abc");
        System.out.println(list);
    }   
}

例7:通过反射写一个通用的设置某个对象的某个属性为指定的值

Test2.java

1package com.mirror.gf;

public class Test2 {

    /**
     * @param args
     * @throws Exception 
     */
    public static void main(String[] args) throws Exception {
        Student s = new Student("张三",20);
        System.out.println(s);

        Tools t = new Tools();
        t.setProperty(s, "name", "李四");
        System.out.println(s);
    }

}

class Student{
    private String name;
    private int age;
    public Student(String name, int age) {
        super();
        this.name = name;
        this.age = age;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }

    public String toString() {
        return "Student [name=" + name + ", age=" + age + "]";
    }
}

Tools.java

1package com.mirror.gf;

import java.lang.reflect.Field;

//此方法可以将obj对象中名为propertyName的属性值设置为value
public class Tools {
    public void setProperty(Object obj,String propertyName,Object value) throws Exception{
        Class clazz = obj.getClass();
        Field f = clazz.getDeclaredField(propertyName);
        f.setAccessible(true);
        f.set(obj, value);
    }
}

运行结果:

Student [name=张三, age=20]

Student [name=李四, age=20]

例8: 已知一个类如下:

1    package com.mirror.gf;

    public class DemoClass {
        public void run(){
            System.out.println("welcome to GuangZhou");
        }
    }

1、写一个properties格式的配置文件,配置类的完整名称;

2、写一个程序,读取这个properties配置文件,获得类的完整名称并加载这个类,用反射的方式运行run方法

配置文件name.properties内容为:com.mirror.gf.DemoClass

Test3.java

1package com.mirror.gf;

import java.io.BufferedReader;
import java.io.FileReader;
import java.lang.reflect.Method;

public class Test3 {

    /**
     * @param args
     * @throws Exception 
     */
    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new FileReader
                ("name.properties"));
        Class clazz = Class.forName(br.readLine());
        DemoClass dc = (DemoClass) clazz.newInstance();
        Method m = clazz.getMethod("run");
        m.invoke(dc);
        //或者直接运行
        dc.run();
    }
}

运行结果:

welcome to GuangZhou

welcome to GuangZhou

19.4 动态代理

动态代理就是通过反射来生成一个代理;利用代理可以在运行时创建一个实现了一组给定接口的新类;

在Java.lang.reflect包中提供了一个Proxy类和一个InvocationHandler接口,通过使用这个类和接口就就可以生成动态代理对象。JDK只能针对接口做代理。

例:

MyInvocationHandler.java

1package com.gf.AutoProxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class MyInvocationHandler implements InvocationHandler {
    private Object target;

    public MyInvocationHandler(Object target){
        this.target = target;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        System.out.println("权限校验");
        method.invoke(target, args);        //执行被代理target对象的方法
        System.out.println("日志记录");
        return null;
    }
}

Student.java

1package com.gf.AutoProxy;

public interface Student {
    public void login();

    public void submit();
}

StudentImp.java

1    package com.gf.AutoProxy;

    public class StudentImp implements Student {
        @Override
        public void login() {
            System.out.println("登录");
        }

        @Override
        public void submit() {
            System.out.println("提交");
        }
    }

Test1.java

1package com.gf.AutoProxy;

import java.lang.reflect.Proxy;

public class Test1 {
    public static void main(String[] args) {
        StudentImp si = new StudentImp();
        si.login();
        si.submit();
        System.out.println("--------------------");
        MyInvocationHandler mi = new MyInvocationHandler(si);
        Student s = (Student) Proxy.newProxyInstance(si.getClass().getClassLoader(), si.getClass().getInterfaces(), mi);
        s.login();
        s.submit();
    }
}

运行结果:

登录

提交

——————–

权限校验

登录

日志记录

权限校验

提交

日志记录

19.5 设计模式

模板方法模式就是定义一个算法的股价,而将具体的算法延迟到子类中实现; 例:

1package com.gf.TemplateMethod;

public class Demo1_Template {

    /**
     * @param args
     */
    public static void main(String[] args) {
        /*long start = System.currentTimeMillis();
        for(int i =0;i<10000;i++){
            System.out.println("1");
        }
        long end = System.currentTimeMillis();
        System.out.println(end-start);*/
        Demo d = new Demo();
        System.out.print(d.getTime());
    }
}

abstract class GetTime{
    //防止被子类重写
    public final long getTime(){
        long start = System.currentTimeMillis();
        code();
        long end = System.currentTimeMillis();
        return end-start;
    }
    abstract void code();
}

class Demo extends GetTime{
    @Override
    void code() {
        int i = 0;
        while(i<10000){
            System.out.print(true);
            i++;
        }   
    }   
}

运行结果:28

20 枚举类的实现

枚举类要用关键字enum所有枚举类都是Enum的子类

枚举类第一行必须是枚举项,最后一个枚举项后的分号可省略,但是如果枚举类有其他项,这个枚举类不能省略;

枚举类可以由构造器,但必须是private,默认也为private;

枚举类也可有抽象方法,但是枚举项必须重写该方法;

枚举类在switch中的应用,如例3

20.1 自定义实现枚举类

枚举概述:将变量的值一一列举出来,变量的值只限于列举出来的值的范围内,例如一周只有7天,一年只有12个月;例1:

Demo1_Enum.java

1package com.gf.enumeration;

public class Demo1_Enum {

    /**
     * @param args
     */
    public static void main(String[] args) {
        //第一种方式:空参构造
        demo();
        //第二种方式:有参构造
        Week2 mon = Week2.MON;
        System.out.println(mon.getName());
        //第三种方式
        Week3 mon1 = Week3.MON;
        mon1.show();
    }

    private static void demo() {
        Week mon = Week.MON;
        Week tue = Week.TUE;
        Week wed = Week.WED;

        System.out.println(mon.getMon());
    }
}

Week.java

1package com.gf.enumeration;

public class Week {
    public static final Week MON = new Week();
    public static final Week TUE = new Week();
    public static final Week WED = new Week();

    private Week(){ }

    public static String getMon() {
        return "周一";
    }
    public static String getTue() {
        return "周二";
    }
    public static String getWed() {
        return "周三";
    }
}

Week2.java

1package com.gf.enumeration;

public class Week2 {
    public static final Week2 MON = new Week2("周一");
    public static final Week2 TUE = new Week2("周二");
    public static final Week2 WED = new Week2("周三");

    private String name;
    private Week2(String name){
        this.name= name;
    }
    public String getName() {
        return name;
    }
}

Week3.java

1package com.gf.enumeration;

public abstract class Week3 {

    public static final Week3 MON = new Week3("周一"){
        public void show(){
            System.out.println("周一");
        }
    };
    public static final Week3 TUE = new Week3("周二"){
        public void show(){
            System.out.println("周二");
        }
    };
    public static final Week3 WED = new Week3("周三"){
        public void show(){
            System.out.println("周三");
        }
    };

    private String name;
    private Week3(String name){
        this.name= name;
    }
    public String getName() {
        return name;
    }

    public abstract void show();
}

运行结果:

周一

周一

周一

20.2 通过enum实现枚举类

enum类中自带toString方法例2:

Demo1_Enum.java

1package com.gf.TemplateMethod2;

public class Demo1_Enum {

    /**
     * @param args
     */
    public static void main(String[] args) {
        //无参构造
        demo();
        //有参构造
        Week2 mon = Week2.MON;
        System.out.println(mon.getName());
        //带抽象方法
        Week3 mon1 = Week3.MON;
        mon1.show();

    }

    private static void demo() {
        Week mon = Week.MON;
        System.out.println(mon);
    }
}

Week.java

1package com.gf.TemplateMethod2;

public enum Week {
    MON,TUE,WED;    
}

Week2.java

1package com.gf.TemplateMethod2;

public enum Week2 {
    MON("星期一"),TUE("星期二"),WED("星期三");   

    private String name;
    private Week2(String name){
        this.name = name;
    }
    public String getName() {
        return name;
    }
}

Week3.java

1package com.gf.TemplateMethod2;

public enum Week3 {
    MON("星期一"){
        public void show(){
            System.out.println("星期一");
        }
    },
    TUE("星期二"){
        public void show(){
            System.out.println("星期二");
        }
    },
    WED("星期三"){
        public void show(){
            System.out.println("星期三");
        }
    };  

    private String name;
    private Week3(String name){
        this.name = name;
    }
    public String getName() {
        return name;
    }

    public abstract void show();
}

运行结果:

MON

星期一

星期一

例3:枚举类在switch中的应用

1package com.gf.TemplateMethod2;

public class Demo1_Enum {

    /**
     * @param args
     */
    public static void main(String[] args) {
        //无参构造
        demo();
        //有参构造
        demo2();
        //带抽象方法
        demo3();

        Week3 mon = Week3.TUE;
        switch(mon){
        case MON:
            System.out.println("星期一");
            break;
        case TUE:
            System.out.println("星期二");
            break;
        case WED:
            System.out.println("星期三");
            break;
        }       
    }

    private static void demo2() {
        Week2 mon = Week2.MON;
        System.out.println(mon.getName());
    }

    private static void demo3() {
        Week3 mon1 = Week3.MON;
        mon1.show();
    }

    private static void demo() {
        Week mon = Week.MON;
        System.out.println(mon);
    }
}

Java基础反射枚举

因为水平有限,难免有疏忽或者不准确的地方,希望大家能够直接指出来,我会及时改正。一切为了知识的分享。

后续会有更多的精彩的内容分享给大家。