目录
一、Java虚拟机(JVM)和类
一个Java程序只有一个主方法。启动Java程序时,首先是启动Java虚拟机,也就是在内存开辟一块空间。启动几个Java程序,就会运行相同数量的Java虚拟机,各个虚拟机之间是没有任何关系的。
二、类的生命周期(重点:类加载过程)
1.类的生命周期(如下图)
2.类加载过程
- 加载:将类的字节码文件加载到Java虚拟机的方法区内存中。
- 连接:把二进制文件数据合并到JRE。包括验证、准备和解析3个步骤。
- 验证:验证字节码文件的格式是否正确,从而保护虚拟机安全(比如将.txt文件改为.class文件),避免虚拟机受到恶意攻击。
- 准备:为类中的静态变量开辟空间,进行默认初始化。
- 解析:把符号引用(即代码形式)替换成指针。指针是指向方法的内存地址的,调用方法时就可以很快找到。
- 初始化:对静态变量进行声明处或静态块处初始化。
三、类的初始化
1.哪些情况下回进行类的初始化?
- Java虚拟机调用主方法会初始化主类(或称为启动类);
- 当创建某个类的对象时会初始化该类;
- 当调用某个类静态成员的时候会初始化该类;(注意:static final修饰且编译器能够确定值,则不会引起初始化,若编译器不能确定值,则会初始化。)
- 初始化子类时会优先初始化父类;
- 使用反射方法强制创建某个类或接口的对象时会进行初始化。
2.示例代码验证
class Base{
// static int sn = 1;
//常量,编译器能直接确定值,不会引发类的初始化
static final int sns = 1;
static{
System.out.println("Base类初始化");
}
}
public class TestDemo1 extends Base{//初始化主类前,先初始化父类Base
static{
System.out.println("主类初始化");
}
public static void main(String[] args) throws ClassNotFoundException {
//创建对象初始化
// Base base = new Base();
//调用静态变量初始化
// System.out.println(Base.sn);
//仅输入1,不会引发Base类的初始化
// System.out.println(Base.sns);
//使用反射方法强制创建某个类或接口的对象时会进行初始化
// Class c = Class.forName("day21_0930.Base");
}
}
四、类加载器
1.类加载器分类
- 根类加载器(Bootstrap ClassLoader):加载核心类库,即只负责D:\Java\JDK1.8\jre\lib下面的类库。
- 扩展类加载器(Extension ClassLoader):加载扩展类库,即只负责D:\Java\JDK1.8\jre\lib\ext下面的类库。
- 系统类(应用类)加载器(System ClassLoader):只负责加载classpath路径下面的类。
- 自定义类加载器(UDF ClassLoader):负责加载用户自己配置的非其他加载器负责的路径下的类。
2.各类加载继承关系
3.父类委托
什么是父类委托?类加载过程中,所有类加载器都是优先找自己的父类加载器进行类加载,若父类未找到需加载的类,则自己再加载。
4.获取类加载器
class Demo1{
static{
//注意,在加载过程中,是不回进行类初始化的,所以静态代码块不会执行。
System.out.println("Demo1类初始化了");
}
}
public class TestDemo2 {
public static void main(String[] args) throws ClassNotFoundException {
//AppClassLoader 获取系统类加载器
// ClassLoader loader = Demo1.class.getClassLoader();
// System.out.println(loader);
//ExtClassLoader 获取扩展类加载器
// ClassLoader loader1 = loader.getParent();
// System.out.println(loader1);
//null 获取根类加载器,输出null,为什么?因为根类加载器使用C写的,所以不展示出来。
// ClassLoader loader2 = loader.getParent().getParent();
// System.out.println(loader2);
//加载并初始化类
Class c = Class.forName("day21_0930.Demo1");
System.out.println(c.getClassLoader());
}
}
5.自定义类加载器
如何自定义类加载器?
首先在非classpath路径下创建一个 .class文件,然后自定义类继承ClassLoader类,并重写findClass()方法即可。
示例:
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
class MyClassLoader extends ClassLoader{
private String path;
public MyClassLoader(String path) {
super();
this.path = path;
}
public MyClassLoader() {
super();
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
Class<?> c = null;
//拼接完整文件路径
//d:\\data\\Hello.class
path = path + name.concat(".class");
File f = new File(path);
FileInputStream fin = null;
try {
fin = new FileInputStream(f);
byte[] b = new byte[fin.available()];
int len = fin.read(b, 0, b.length);
//解释成方法区能识别的数据结构
c = this.defineClass(name, b, 0, len);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fin != null) {
try {
fin.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return c;
}
}
public class TestDemo4 {
public static void main(String[] args) throws ClassNotFoundException {
//创建自定义类加载器
MyClassLoader loader = new MyClassLoader("d:\\data\\");
//使用自定义类加载器
Class.forName("Hello", true, loader);
}
}
五、安全管理器
1.什么是安全管理器?
原理:通过安全管理器判断读取流对源文件是否有权限操作,若有 ,则执行读文件操作,否则就会抛出异常。
2.如何实现自定义安全管理器?
重写SecurityManager 来控制自定义程序的安全性。
示例:
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
/**自定义安全管理器*/
class MySecurityManager extends SecurityManager{
@Override
public void checkRead(String file) {
if (file.endsWith(".java")) {
//没权访问,就抛出安全异常
throw new SecurityException("没有读取权限");
}
}
}
public class TestDemo5 {
public static void main(String[] args) throws IOException {
//设置安全管理
System.setSecurityManager(new MySecurityManager());
File f = new File("d:\\data\\Hello.java");
FileInputStream fin = new FileInputStream(f);
int temp;
while((temp = fin.read()) != -1){
System.out.println(temp);
}
fin.close();
}
}
六、反射
1.什么是反射?
利用类的Class对象,调用Class类的方法获得对应类的所有信息,比如属性、方法、构造器等。
2.反射的作用
- 获取对象的类信息并修改;
- 打破访问权限控制。
3.如何使用反射?
示例:通过反射技术,获取类的属性、方法和构造器并操控它们。
class Demo6{
protected int no;
private String name;
public Demo6() {
super();
System.out.println("无参构造");
}
public Demo6(int no, String name) {
super();
this.no = no;
this.name = name;
System.out.println("有参构造:" + no + ":" + name);
}
public void show(){
System.out.println("无参方法show");
}
public String disp(int n, String m){
return "有参方法:" + n + m;
}
}
public class TestDemo6 {
public static void main(String[] args) throws Exception {
//获取Class对象的三种方式
//1.类型名.class
// Class<Demo6> c = Demo6.class;
//2.对象.getClass()
// Class<? extends Demo6> c = new Demo6().getClass();
//3.Class.forName()
Class<?> c = Class.forName("day21_0930.Demo6");
/*****************获取类的属性*********************/
//getFields()只能获得公有属性
// Field[] fields = c.getFields();
//getDeclaredFields() 获取所有属性,包括private的
Field[] fields = c.getDeclaredFields();
for(Field f : fields){
//获取属性名字
System.out.println(f.getName());
//获取属性类型
System.out.println(f.getType());
//获取属性访问修饰符,注意转换
System.out.println(Modifier.toString(f.getModifiers()));
}
//根据属性名称获取属性(Field)对象
Field field1 = c.getDeclaredField("name");
Field field2 = c.getDeclaredField("no");
//根据类对象获取该类的实例对象
Object obj = c.newInstance();
//设置为true,即可攻破访问权限
field1.setAccessible(true);
//给对象的该属性赋值
field1.set(obj, "Tom");
field2.set(obj, 10000);
//获取对象的该属性值
System.out.println(field1.get(obj));
System.out.println(field2.getInt(obj));
/*****************获取类的方法*********************/
System.out.println("****************************");
//获取方法
//获取该类的所有自己的方法,而不获取父类的方法
Method[] methods = c.getDeclaredMethods();
for(Method m : methods){
//获取方法名
System.out.println(m.getName());
//获取方法访问修饰符
System.out.println(Modifier.toString(m.getModifiers()));
//获取返回值类型
System.out.println(m.getReturnType());
//获取方法参数列表
Parameter[] paras = m.getParameters();
System.out.println(Arrays.toString(paras));
}
//根据方法名和参数列表(若无参数,则不写)获取方法
Method m1 = c.getDeclaredMethod("show");
Method m2 = c.getDeclaredMethod("disp", int.class,String.class);
//执行特定对象的该方法,注意别传错参数了,否则异常
m1.invoke(obj);
String str = (String)m2.invoke(obj,123,"Jiangw");
System.out.println(str);
/*****************获取类的构造器*********************/
System.out.println("****************************");
//获取所有构造器
Constructor<?>[] cons = c.getDeclaredConstructors();
for(Constructor<?> con : cons){
//获取构造器名字
System.out.println(con.getName());
//获取构造器访问修饰符
System.out.println(Modifier.toString(con.getModifiers()));
//获取构造器参数列表
Parameter[] ps = con.getParameters();
System.out.println(Arrays.toString(ps));
}
//根据参数列表获取指定构造器
Constructor<?> c1 = c.getDeclaredConstructor();
Constructor<?> c2 = c.getDeclaredConstructor(int.class,String.class);
//调用构造器
c1.newInstance();
c2.newInstance(10003,"chenjun");
}
}