类的加载
类加载的过程
当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载,连接,初始化三步来实现对这个类进行初始化。
加载
就是指将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;
}