类的加载和反射及枚举简述

类的加载

类加载的过程
当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载,连接,初始化三步来实现对这个类进行初始化。
加载
就是指将class文件读入内存,并为之创建一个Class对象(字节码)。任何类被使用时系统都会建立唯一的一个Class对象(字节码)。
连接
验证 是否有正确的内部结构,并和其他类协调一致
准备 负责为类的静态成员分配内存,并设置默认初始化值
解析 将类的二进制数据中的符号引用替换为直接引用
初始化
就是给属性赋值

类加载时机
加载的原则-用到的时候才加载
比如:
创建类的实例
访问类的静态变量,或者为静态变量赋值
调用类的静态方法
初始化某个类的子类
直接使用java.exe命令来运行某个主类
使用反射方式强制创建某个类或接口对应的java.lang.Class对象

类加载器
类加载器的概述
负责将.class文件加载到内存中,并为之生成对应的Class对象。虽然我们不需要关心类加载机制,但是了解这个机制我们就能更好的理解程序的运行。
类加载器的分类
Bootstrap ClassLoader 根类加载器
Extension ClassLoader 扩展类加载器
System ClassLoader 系统类加载器

各类加载器的作用
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包和类路径

反射

反射概述
JAVA反射机制是在运行状态中,对于任意一个类都能够知道这个类的所有属性和方法;
对于任意一个对象,都能通过反射够调用它的任意一个方法和属性;
要想解剖一个类,必须先要获取到该类的字节码文件对象。

获取字节码对象三种方式
Object类的getClass()方法
静态属性class,锁对象
Class类中静态方法forName()
代码:

public static void main(String[] args) throws ClassNotFoundException {
		//1.Object类的getClass()方法
		Person p = new Person();
		Class clz1 = p.getClass();
		
		//2.静态属性clas
		Class clz2 = Person.class;
		
		//3.Class类中静态方法forName()
		/**
		 * 参数 forName(String className) 传类全路径(包名+类名)
		 */
		Class clz3 = Class.forName("反射.Person");
		
		//4.字节码对象在内存中只有一个
		System.out.println("clz1:" + clz1.hashCode());
		System.out.println("clz2:" + clz2.hashCode());
		System.out.println("clz3:" + clz3.hashCode());
	}

读取info.txt文件第一行:类全路径

        //1.字符流读取info.txt文件内容
		FileReader fr = new FileReader("info.txt");
		BufferedReader br = new BufferedReader(fr);
		String className = br.readLine();
		br.close();
		
		//2.获取字节码对象
		Class clz = Class.forName(className);
		System.out.println(clz);

通过反射获取参构造方法
使用要点:
1.如果要使用反射,先要获取字节码对象
2.通过字节码对象的getConstructor()可以获取到构造方法对象
3.构造方法对象(Contructor),有个newInstance方法可以创建该类对象
4.反射是在java.lang.reflect这个包中
5.反射的作用一般是用于写框架(ssh,ssm)
代码

        //获取字节码对象
		Class clz = Teacher.class;
		
		//获取无参构造方法对象
		Constructor c1 = clz.getConstructor();
	
		//通过无参构造方法对象创建类对象
		Teacher teacher1 = (Teacher) c1.newInstance();
		System.out.println("teacher1:" + teacher1);
		
		// 获取有参构造方法对象
		/**
		 * parameterType 参数类型:类的字节码
		 */
		Constructor c2 = clz.getConstructor(String.class,String.class);
		/通过有参构造方法对象创建类对象
		Teacher teacher2 = (Teacher) c2.newInstance("ldg","梅州");//相当于调用new Teacher("ldg","梅州")
		System.out.println("teacher2:" + teacher2);
		
		Constructor c3 = clz.getConstructor(String.class,double.class);
		Teacher teacher3 = (Teacher) c3.newInstance("ldg",1.70);//相当于调用new Teacher("ldg","1.70")
		System.out.println("teacher3:" + teacher3);

通过反射获取类属性
使用要点
1.Class的 public Field getField(String name)方法可以获取类中的指定字段(可见的),
2.如果是私有的,可以用 public Field getDeclaredField(String name)方法获取
3.通过Field 中的public void set(Object obj, Object value)方法可以设置指定对象上该字段的值
4.如果是私有的需要先调用public void setAccessible(boolean flag)设置访问权限,true 表示允许访问

代码:

        //1.获取字节码对象
		Class clz = Teacher.class;
		
		//2.获取color字段
		Field colorField = clz.getField("color");
		System.out.println(colorField);
		
		//3.通过反射给可见字段赋值
		Teacher teacher = new Teacher();
		//teacher.color = "白色";
		colorField.set(teacher, "黄色");
		
		//4.获取私有name属性
		Field nameField = clz.getDeclaredField("name");	
		System.out.println(nameField);
		
		//5.通过反射给私有属性赋值
		nameField.setAccessible(true);//设置私有属性可以访问
		nameField.set(teacher, "gyf");
		
		System.out.println(teacher);
		
		//6.通过反射获取私有属性的值
		Object value = nameField.get(teacher);
		System.out.println(value);

通过反射获取方法并使用
使用要点
1.反射中通过Method类描述方法【构造方法:Contructor,字段:Field】
2.通过Class的getMethod可以获取一个方法
3.通过getDeclaredMethod可以获取私有方法
4.如果要调用私有方法,设置访问权限setAccessible
代码:

       //获取字节码对象 
		Class clz = Teacher.class;
				
		Teacher teacher = new Teacher();
	/*	teacher.say1();
		teacher.say2("小黄鸭");
		teacher.say3("小黄鸭",2);*/
		
		// 通过反射调用方法
		//获取无参方法
		Method m1 = clz.getDeclaredMethod("say1");
		//2.1 获取有参方法
		Method m2 = clz.getDeclaredMethod("say2", String.class);
		Method m3 = clz.getDeclaredMethod("say3", String.class,int.class);
	
		//设置私有方法可以访问
		m1.setAccessible(true);
		m2.setAccessible(true);
		m3.setAccessible(true);
		//3.2 调用方法
		m1.invoke(teacher);
		m2.invoke(teacher, "小猪");
		m3.invoke(teacher, "小狗",98);

越过泛型的检查代码:

        //1.声明泛型集合
		List<Integer> list = new ArrayList<Integer>();
		
		list.add(110);
		list.add(120);
		list.add(130);
		//list.add("ldg");
		
		//2.通过反射往集合添加字符串
		//2.1 获取字节码对象(Class)
		Class clz = list.getClass();
		
		//2.2 通过反射获取方法
		Method method = clz.getMethod("add", Object.class);
		
		//2.3 调用方法就可以添加字符串 
		method.invoke(list, "ldg");
		//
		System.out.println(list);

枚举

概述
1.枚举是指将变量的值一一列出来,可以称为『数据集』 。
举例:一周只有7天,一年只有12个月,一年有四个季节等。
2.Java中enum通过声明的类称为枚举类
3.枚举其实就是限定范围,防止不应该发生的事情发生
4.枚举注意事项
定义枚举类要用关键字enum
所有枚举类都是Enum的子类
枚举类的第一行上必须是枚举项,最后一个枚举项后的分号是可以省略的,但是如果枚举类有其她的东西,这个分号就不能省略。建议不要省略
枚举类可以有构造器,但必须是private的,它默认的也是private的。
枚举类也可以有抽象方法,但是枚举项必须重写该方法
枚举类也可以有方法.
5. 枚举是一个特殊类

枚举的声明


public class 枚举 {

	public static void main(String[] args) {
		//普通使用枚举
        System.out.println(Season.Autumn);
        //调用用枚举项中的方法
        Season.Autumn.test();
        //调用枚举中的方法
        Season.Autumn.test1();
	}

}
//枚举简单声明
//enum Season{
//	Spring,Summer,Autumn,Winter;
//}
enum Season{
	//枚举项的声明是要调用构造方法的 且只要枚举被加载了 就声明所有的枚举项(对象)
	Spring("春天") {
		@Override
		public void test() {
		System.out.println("我是春天");
			
		}
	},Summer("夏天") {
		@Override
		public void test() {
		System.out.println("我是夏天");
			
		}
	},Autumn("秋天") {
		@Override
		public void test() {
		System.out.println("我是秋天");
			
		}
	},Winter {
		@Override
		public void test() {
		System.out.println("我是冬天");
			
		}
	};
	private   Season(String  name)
	{
		System.out.println("有参构造方法");
	}
	private   Season()
	{
		System.out.println("无参构造方法");
	}
	//可以声明抽象方法
	public  abstract  void test() ;
	//声明普通方法
	public  void   test1() {System.out.println("我是枚举");};
}

枚举的常见方法
int ordinal() 枚举项都有索引,从0开始
int compareTo(E o)
String name() 枚举项名称
String toString()
T valueOf(Class type,String name)通过字节码对象获取枚举对象
values() 此方法虽然在JDK文档中查找不到,但每个枚举类都具有该方法,它遍历枚举类的所有枚举值非常方便

public class 枚举 {

	public static void main(String[] args) {
		//枚举类的常见方法
		//1.获取枚举对象
		Season s1 = Season.SPRING;
		Season s2 = Season.WINTER;
		//获取枚举项索引
		System.out.println("s1:" + s1.ordinal());
		System.out.println("s2:" + s2.ordinal());
		
		//2.比较枚举[索引相减]
		System.out.println(s1.compareTo(s2));
		
		//3.打印枚举项名称
		System.out.println("s1 name:" + s1.name());
		System.out.println("s1:" + s1.toString());//枚举项名称
		
		//4.通过字节码对象获取枚举对象【没啥用】
		Season s3 = Season.valueOf(Season.class, "SPRING");
		System.out.println("s3:" + s3);
		
		//5.类方法,.values() 返回枚举数组
		System.out.println("枚举的遍历");
		Season[] seasons = Season.values();
		for(Season s : seasons){
			System.out.println(s);
		}
	}
}

enum Season{
	SPRING,SUMMER,AUTUMN,WINTER;
}

发布了23 篇原创文章 · 获赞 17 · 访问量 1276

猜你喜欢

转载自blog.csdn.net/qq_41587409/article/details/105499570