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.1、什么是枚举
4.2、JDK1.5提供的枚举
4.3、注意事项: