基础知识27_枚举、反射、动态代理


1、反射
1.1、概述
1.2、获取构造
1.3、获取字段
1.4、获取方法
2、动态代理
2.1、概述
2.2、动态类
2.3、动态对象
3、枚举
3.1、概念
3.2、模拟枚举
3.3、JDK1.5提供的枚举
3.4、枚举类的细节
3.5、常用方法

课堂笔记


1、类加载器(ClassLoader)
1.1、概述


/*
 * 演示:类加载器的基本使用
 * Bootstrap 根类加载器
 * ExtClassLoader 扩展类加载器
 * AppClassLoader 系统类加载器
 * 获取类的类加载器:
 * 类名.class.getClassLoader() 获取自己的类加载器
 * 问题来了:类加载器也是类,类加载器又是被谁加载的呢??
 * 我们需要拿到类加载器的类加载器:
 * getParent();
 * 显然,第一个类加载器必须不是Java类,用来加载其它类,它就是BootStrap,不是Java类,而是用c++
 */
public class ClassLoaderDemo01 {
public static void main(String[] args) {
// 获取当前类的类加载器
ClassLoader loader = ClassLoaderDemo01.class.getClassLoader();
System.out.println(loader);// AppClassLoader

// 找APP的爹:
ClassLoader parent = loader.getParent();
System.out.println(parent);// ExtClassLoader

// 找Ext的爹:
ClassLoader grandParent = parent.getParent();
System.out.println(grandParent);// null
}
}


1.2、使用类加载器管理配置文件
/*
 * 演示:类加载器加载配置文件
 * 分析:
 * 类加载器:可以扫描classpath下的所有.class文件,把需要的.class文件加载到内存中。变成字节码文件对象。
 * 既然可以扫class文件,理论上也可以扫普通配置文件,问题是:人家愿不愿意帮我们扫。类加载器可以帮我们扫描。
 * 用类加载器加载文件:
 * 类加载器.getResourceAsStream("文件名") ,类加载器加载配置文件,相对路径是从classpath(src)下来找
 * 
 *  类名.class.getResourceAsStream("文件名"),用类的字节码文件来加载,这种方式,如果是相对路径,会从类所在的包开始找。
 *  如果要想从classpath根目录找,在路径前加 /
 */
public class ClassLoaderDemo02 {
public static void main(String[] args) throws IOException {
// 创建集合
Properties prop = new Properties();
// FileInputStream fis = new FileInputStream("src/chat.cfg");
// 获取类加载器:
InputStream in = ClassLoaderDemo02.class.getClassLoader().getResourceAsStream("chat.cfg");
// InputStream in = ClassLoaderDemo02.class.getResourceAsStream("/chat.cfg");
// 加载数据
prop.load(in);
// 获取数据
String ip = prop.getProperty("ip");
String port = prop.getProperty("port");
System.out.println(ip + " -- " + port);
}
}


2、反射
2.1、反射概述
Class类 是 反射的基石!


2.1.1、Class类介绍


问题1:有各种各样的人,人这种事物我们如何表示?有各种各样的汽车,用什么表示?计算机中有各种文件和文件夹,用什么表示?
Person类,Car类,File类
这么多各种各样的类,类也是一种事物,这种事物用什么描述:Class类
问题2:Person类的对象,代表一个具体的人,File类的对象代表一个具体的文件,Class类的对象代表什么?
一个类,在硬盘中表现的是.class文件,类加载器会把.class文件加载到内存,开辟一片空间,
描述这个.class文件,这个空间及其中的内容就是一个Class类的对象,字节码文件对象
问题:如何获取Class对象呢?


2.1.2、获取Class的三种方式
/*
 * 演示:Class的三种获取方式
 * 方式1:类名.class
 * 方式2:对象.getClass()
 * 方式3:static Class forName(String className) 根据类名来获取Class对象
 * 
 * 获取类的名称:
 * getName() 获取类的全名称
 * getSimpleName() 获取类的简称
 * 我们用哪一种?
 * 如果是自己玩,用前两种。
 * 如果是开发,用第3种。第三种扩展性更好
 * 
 * 这三种方式获取的都是同一份字节码文件对象。任何一个类的字节码文件,永远只有一个。
 */
public class ClassDemo01 {
public static void main(String[] args) throws ClassNotFoundException {
// 方式1:类名.class
Class c1 = String.class;
System.out.println(c1.getSimpleName());
System.out.println(c1.getName());
System.out.println("-----------------------");
// 方式2:对象.getClass()
Class c2 = "h".getClass();
System.out.println(c2.getSimpleName());
System.out.println(c2.getName());
System.out.println("-----------------------");
// 方式3:static Class forName(String className) 根据类名来获取Class对象
// Class c3 = Class.forName("String");// java.lang.ClassNotFoundException: String
Class c3 = Class.forName("java.lang.String");
System.out.println(c3.getSimpleName());
System.out.println(c3.getName());

System.out.println(c1 == c2);// true
System.out.println(c1 == c3);// true
}
}
2.1.3、预定义对象:
/*
 * 演示:Class的9种预定义对象
 * 8种基本类型.class 再加上 void.class
 * 
 * 判断是否是原始类型:Class.isPrimitive() 判断是否是预定义对象
 * 判断是否是数组:Class.isArray()
 */
public class ClassDemo02 {
public static void main(String[] args) {
Class int_clazz = int.class;
System.out.println(int_clazz);

Class char_clazz = char.class;
System.out.println(char_clazz);

Class void_clazz = void.class;
System.out.println(void_clazz);

Class integer_clazz = Integer.class;
System.out.println(int_clazz == integer_clazz);// false
System.out.println(int_clazz == Integer.TYPE);// true

Class arr_clazz = int[].class;
System.out.println(arr_clazz);// [I

System.out.println(int_clazz.isPrimitive());// true
System.out.println(arr_clazz.isPrimitive());// false
System.out.println(arr_clazz.isArray());// true
}
}
2.1.4、反射的概念




2.2、使用反射获取构造函数(Constructor)
/*
 * 演示:反射获取构造函数:
 * getConstructors() 获取所有公共构造函数
 * getDeclaredConstructors() 获取所有构造函数
 * getConstructor(Class ... parameterTypes) 根据参数列表获取指定的构造函数
 * 判断一个函数的依据:方法名,参数列表。
 * getDeclaredConstructor(Class ... parameterTypes) 根据参数列表获取指定的私有构造
 * 获取构造函数有什么用?创建对象
 * Object newInstance(Object... initargs) 使用此 Constructor 对象表示的构造方法来创建对象 
 * 注意:
 * 1)一切私有成员,都可以用setAccessible(true)来暴力访问!
 * 2)newInstance方法在使用的时候,参数必须与获取构造器时传递的类型一致!
 * 
 * 在Class类中定义了一个方法:
 * newInstance() 创建此 Class 对象所表示的类的一个新实例
 * 注意,这个方法默认用的是无参构造。如果一个类,没有无参构造,无法使用该方法。
 */
public class ClassDemo03 {
public static void main(String[] args) throws NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
// 获取一个Class对象
Class c = File.class;

// getConstructors() 获取所有构造函数
// Constructor[] cons = c.getConstructors();
// for( Constructor con : cons){
// System.out.println(con);
// }
// getDeclaredConstructors() 获取所有构造函数
// Constructor[] cons = c.getDeclaredConstructors();
// for(Constructor con : cons){
// System.out.println(con);
// }
// getConstructor(Class ... parameterTypes) 根据参数列表获取指定的构造函数
// 获取File(String name)
// Constructor con = c.getConstructor(String.class);
// System.out.println(con);

// getDeclaredConstructor(Class ... parameterTypes) 根据参数列表获取指定的私有构造
Constructor con = c.getDeclaredConstructor(String.class,File.class);
System.out.println(con);// private java.io.File(java.lang.String,java.io.File)

// Object newInstance(Object... initargs) 使用此 Constructor 对象表示的构造方法来创建对象
// 设置暴力访问
con.setAccessible(true);
File f = (File) con.newInstance("柳岩.jpg",new File("D:\\test"));
System.out.println(f.getAbsolutePath());// IllegalAccessException

//newInstance() 创建此 Class 对象所表示的类的一个新实例
String str = String.class.newInstance();
System.out.println("str:" + str);
}
}
2.3、反射获取成员变量(Field)
/*
 * 演示:反射获取成员变量
 * getFields() 获取所有的公共字段
 * getDeclaredFields() 获取所有字段
 * getField(String name) 获取单个公共字段
 * getDeclaredField() 获取单个字段
 * 字段有什么用?可以获取和修改某个对象的字段值
 * Object get(Object obj) 返回指定对象上此 Field 表示的字段的值 
 * set(Object obj, Object value)将指定对象变量上此 Field 对象表示的字段设置为指定的新值
 */
public class ClassDemo04 {
public static void main(String[] args) throws Exception {
Class c = Person.class;
// getFields() 获取所有的公共字段
// Field[] fs = c.getFields();
// for(Field f : fs){
// System.out.println(f);// public int cn.itcast.demo02.Person.age
// }
// getDeclaredFields() 获取所有字段
// Field[] fs = c.getDeclaredFields();
// for(Field f : fs){
// System.out.println(f);
// }
// getField(String name) 获取单个公共字段
Field f1 = c.getField("age");
System.out.println(f1);// public int cn.itcast.demo02.Person.age

// getDeclaredField() 获取单个字段
// 获取addr字段
Field f2 = c.getDeclaredField("addr");
System.out.println(f2);// private java.lang.String cn.itcast.demo02.Person.addr
// 创建一个Person对象
Person p = new Person("柳岩", 21, "北京");

// 获取Person中的字段值
// Object get(Object obj) 返回指定对象上此 Field 表示的字段的值 
f2.setAccessible(true);
String addr = (String) f2.get(p);
System.out.println(addr);

// 修改Person的字段值
// set(Object obj, Object value)将指定对象变量上此 Field 对象表示的字段设置为指定的新值
f2.set(p, "上海市航都路18号5号楼611");
System.out.println(f2.get(p));
}
}
2.4、反射获取成员方法
/*
 * 演示:反射获取成员方法
 * getMethods() 获取所有公有方法,包含了继承自父类的方法。
 * getDeclaredMethods() 获取所有方法,不包含继承自父类但是没有重写的方法
 * getMethod(String name,Class ... parameterTypes) 获取某个公有方法
 * getDeclaredMethod(String name,Class ... parameterTypes) 获取某个方法
 * 获取成员方法有什么用?调用方法。
 * Object invoke(Object obj, Object... args)  调用指定对象上的当前method表示的方法
 * 注意:
 * 1)调用invoke时,传递的参数,必须和获取method时参数类型一致
 * 2)如果调用静态方法,那么invoke的第一个参数可以为null
 * 3)如果调用的是无参方法,那么invoke的第二个参数可以不传。
 */
public class ClassDemo05 {
public static void main(String[] args) throws Exception {
Class c = String.class;
// getMethods() 获取所有公有方法
// Method[] ms = c.getMethods();
// for(Method m : ms){
// System.out.println(m);
// }
// System.out.println(ms.length);
// getDeclaredMethods() 获取所有方法
// Method[] ms = c.getDeclaredMethods();
// for(Method m : ms){
// System.out.println(m);
// }
// System.out.println(ms.length);

// getMethod(String name,Class ... parameterTypes) 获取某个公有方法
// 获取一个charAt方法
Method m = c.getMethod("charAt", int.class);
System.out.println(m);// public char java.lang.String.charAt(int)
// 需求:调用charAt方法
// 不用反射:
String str = "hello";
char c1 = str.charAt(1);
System.out.println(c1);
// Object invoke(Object obj, Object... args)  调用指定对象上的当前method表示的方法
// 用反射
char c2 = (char) m.invoke(str, 1);
System.out.println(c2);

// 调用静态方法
// static String valueOf(double d)  
Method m2 = c.getMethod("valueOf", double.class);
String s = (String) m2.invoke(null, 12.34);
System.out.println(s);// 12.34

// 调用无参方法无返回值方法
Class pc = Person.class;
Person p = new Person("柳岩", 21, "上海611");
Method m3 = pc.getMethod("show");// 第二个参数是可变参数,所以可以不写
m3.invoke(p,null);

}
}




2.5、应用举例:
2.5.1、获取一个对象的所有字段,把其中String类型的字段值中的’h’全改成’f’ 
/*
 * 演示:获取一个对象的所有字段,把其中String类型的字段值中的’h’全改成’f’ 
 * 思路:
 * A:获取对象所属类的字节码文件对象
 * B:获取所有字段的数组
 * C:遍历数组,取出每个字段
 * D:判断字段是否是String类型
 * 否:不管
 * 是:获取该对象上该字段的值,是一个字符串
 * E:把这个字符串中的h替换成f
 * F:把修改后的字符串设置给对象中
 */
public class ClassDemo06 {
public static void main(String[] args) throws Exception {
Person p = new Person("huanghaibo", 40, "huzhou");
// A:获取对象所属类的字节码文件对象
Class c = p.getClass();
// B:获取所有字段的数组
Field[] fs = c.getDeclaredFields();
// C:遍历数组,取出每个字段
for( Field f : fs){
// D:判断字段是否是String类型
if(f.getType() == String.class){
// 设置暴力访问
f.setAccessible(true);
// 是:获取该对象上该字段的值,是一个字符串
String str = (String) f.get(p);
// E:把这个字符串中的h替换成f
str = str.replace('h', 'f');
// F:把修改后的字符串设置给对象中
f.set(p, str);
}
}
System.out.println(p);
}
}




2.5.2、我给你ArrayList<Integer>的一个对象,我想在这个集合中添加一个字符串数据,如何实现呢? 
/*
 * 演示:我给你ArrayList<Integer>的一个对象,我想在这个集合中添加一个字符串数据,如何实现呢? 
 * 原理:
 * 泛型是编译器技术,字节码文件中是没有泛型的。所以,我们通过反射来调用add方法,是不检查泛型的。
 */
public class ClassDemo07 {
public static void main(String[] args) throws Exception {
// 创建集合
ArrayList<Integer> al = new ArrayList<>();
// 添加元素
al.add(100);
// al.add("22");

// 利用反射来添加元素
// A:获取类的字节码文件对象
Class c = al.getClass();
// B:获取add方法
Method m = c.getMethod("add", Object.class);
// C:调用add方法
m.invoke(al, "hello");
m.invoke(al, "world");
m.invoke(al, "java");
// 遍历集合
System.out.println(al);// [100, hello, world, java]
}
}




2.5.3、通过配置文件实现类的切换 
测试类:
/*
 * 测试
 */
public class Test {
public static void main(String[] args) throws Exception {
// Properties prop = new Properties();
// // 加载配置文件
// InputStream in = Test.class.getResourceAsStream("/chat.cfg");
// prop.load(in);
// // 获取配置文件中的类名称
// String className = prop.getProperty(Math.class.getName());
// // 获取字节码文件对象
// Class c = Class.forName(className);
// // 创建对象
// Math m = (Math) c.newInstance();

Math m = BeanFactory.getInstance(Math.class);

System.out.println("sum:" + m.getSum(100));

System.out.println("jc:" + m.getJC(5));

Collection al = BeanFactory.getInstance(Collection.class);
al.add("hello");
al.add("world");
al.add("java");
al.add("java");
System.out.println(al);
}
}




Bean工厂:
public class BeanFactory {

public static <T> T getInstance(Class<T> clazz){
try {
Properties prop = new Properties();
// 加载配置文件
InputStream in = Test.class.getResourceAsStream("/chat.cfg");
prop.load(in);
// 获取配置文件中的类名称
String className = prop.getProperty(clazz.getName());
// 获取字节码文件对象
Class c = Class.forName(className);
// 创建对象
return (T) c.newInstance();
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("配置文件出错,请核对。");
}
}
}




3、动态代理
3.1、概念
代理模式:
被代理对象:
public class AngelaBaby implements Popstar {


@Override
public void sing(double money) {
System.out.println("AngelaBaby唱了一首:山路十八弯");
System.out.println("挣了" + money + "RMB");
}


@Override
public void liveShow(double money) {
System.out.println("AngelaBaby参加了Running man");
System.out.println("挣了" + money + "RMB");
}


}


代理对象:
/**
 * 经纪人
 * @author zhang
 */
public class Huge implements Popstar{

// 代理一个明星,就是持有明星的对象
private Popstar star;

public Huge(Popstar star){
this.star = star;
}


@Override
public void sing(double money) {
if(money < 500000){
System.out.println("滚,一边玩泥巴去!");
return;
}
System.out.println("抽取了"+ money * 0.2+"中介费");
star.sing(0.8 * money);
}


@Override
public void liveShow(double money) {
if(money < 500000){
System.out.println("滚,一边玩泥巴去!");
return;
}
System.out.println("抽取了"+ money * 0.2+"中介费");
star.liveShow(0.8 * money);
}


}


/*
 * 演示:代理模式
 * 代理模式:自己的事情,请别人来管理。
 * 代理模式与装饰模式的区别和联系?
 * 联系:无论代理模式还是装饰模式,都需要持有被装饰或被代理的类的对象
 * 区别:
 * 代理模式:控制被代理对象的访问
 * 装饰模式:加强被装饰的类的功能
 */
public class Test {
public static void main(String[] args) {
AngelaBaby ab = new AngelaBaby();

// 创建代理对象
Huge h = new Huge(ab);

h.sing(10);

h.liveShow(1000000);
}
}

3.2、动态生成一个类

3.3、动态生成一个对象


4、枚举
4.1、什么是枚举
4.2、JDK1.5提供的枚举
4.3、注意事项:
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值