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基础反射枚举
因为水平有限,难免有疏忽或者不准确的地方,希望大家能够直接指出来,我会及时改正。一切为了知识的分享。
后续会有更多的精彩的内容分享给大家。
支付宝扫一扫
微信扫一扫
