学习目标:
一周掌握 Java 入门知识
学习内容:
正式学习
Java基本知识
Java基本语法
java基本语法在这里不做详细说明,这里提供一些学习资源
1、菜鸟教程:https://www.runoob.com/java/java-tutorial.html
2、博客:https://shoka.lostyu.me/categories/computer-science/java/
java类加载原理
Java加载机制
一、加载简略过程
虚拟机将类的数据从Class文件加载到内存,并对数据进行校验,转换解析,和初始化最终形成Java虚拟机可以使用的Java类型。
注意这里的几个阶段是按顺序开始,而不是按顺序进行或完成,因为这些阶段通常都是互相交叉地混合进行的,通常在一个阶段执行的过程中调用或激活另一个阶段。
二、核心机
JVM类加载机制
•全盘负责,当一个类加载器负责加载某个Class时,该Class所依赖的和引用的其他Class也将由该类加载器负责载入,除非显示使用另外一个类加载器来载入
•父类委托,先让父类加载器试图加载该类,只有在父类加载器无法加载该类时才尝试从自己的类路径中加载该类
•缓存机制,缓存机制将会保证所有加载过的Class都会被缓存,当程序中需要使用某个Class时,类加载器先从缓存区寻找该Class,只有缓存区不存在,系统才会读取该类对应的二进制数据,并将其转换成Class对象,存入缓存区。这就是为什么修改了Class后,必须重启JVM,程序的修改才会生效
Java生命周期
注意这里的几个阶段是按顺序开始,而不是按顺序进行或完成,因为这些阶段通常都是互相交叉地混合进行的,通常在一个阶段执行的过程中调用或激活另一个阶段。
加载步骤
JVM将类的加载分为三个步骤装载、连接以及初始化。
- 装载(加载):Java 将字节码数据从不同的数据源读取到 JVM 中,并映射为 JVM 认可的数据结构(Class 对象),这里的数据源可能是各种各样的形态,比如 jar 文件,class 文件,甚至是网络数据源等;如果输入数据不是 ClassFile 的结构,则会抛出 ClassFormatError。加载阶段是用户参与的阶段,我们可以自定义类加载器,去实现自己的类加载过程。
- 连接:连接分为三个步骤,分别是验证、准备以及解析。
1、验证:目的在于确保class文件的字节流中包含信息符合当前虚拟机要求,不会危害虚拟机自身的安全,主要包括四种验证:文件格式的验证,元数据的验证,字节码验证,符号引用验证。
这一步骤非常重要。如果某些黑客写了一个.恶意class文件,让JVM执行,后果不堪设想。
2、准备:为类的静态变量分配内存,并将其初始化为默认值
3、解析:解析阶段是虚拟机将常量池内的符号引用替换为直接引用的过程,解析动作主要针对类或接口、字段、类方法、接口方法、方法类型、方法句柄和调用点限定符7类符号引用进行。
符号引用就是一组符号来描述目标,可以是任何字面量。
直接引用就是直接指向目标的指针、相对偏移量或一个间接定位到目标的句柄。 - 初始化:这里是类记载的最后阶段,如果该类具有父类就进行对父类进行初始化,执行其静态初始化器(静态代码块)和静态初始化成员变量。
JVM初始化步骤
1、假如这个类还没有被加载和连接,则程序先加载并连接该类
2、假如该类的直接父类还没有被初始化,则先初始化其直接父类
3、假如类中有初始化语句,则系统依次执行这些初始化语句
Java类加载器
一、分类
有两种类加载器:
1 启动类加载器(Bootstrap ClassLoader)
由C++语言实现(针对HotSpot VM),负责将存放在<JAVA_HOME>lib目录或-Xbootclasspath参数指定的路径中的类库加载到JVM内存中,像java.lang.、java.util.、java.io.*等等。可以通过vm参数“-XX:+TraceClassLoading”来获取类加载信息。我们无法直接使用该类加载器。
2 其他类加载器(Java语言实现)
1)扩展类加载器(Extension ClassLoader)
负责加载<JAVA_HOME>libext目录或java.ext.dirs系统变量指定的路径中的所有类库。我们可以直接使用这个类加载器。
2)应用程序类加载器(Application ClassLoader),或者叫系统类加载器
负责加载用户类路径(classpath)上的指定类库,我们可以直接使用这个类加载器。一般情况,如果我们没有自定义类加载器默认就是用这个加载器。
3)自定义类加载器
通过继承ClassLoader类实现,主要重写findClass方法。
二、加载顺序
在JVM虚拟机中,如果一个类加载器收到类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器完成。每个类加载器都是如此,只有当父加载器在自己的搜索范围内找不到指定的类时(即ClassNotFoundException),子加载器才会尝试自己去加载。
也就是说,对于每个类加载器,只有父类(依次递归)找不到时,才自己加载 。这就是双亲委派模型。
Java反射原理
反射定义
(1)Java反射机制的核心是在程序运行时动态加载类并获取类的详细信息,从而操作类或对象的属性和方法。本质是JVM得到class对象之后,再通过class对象进行反编译,从而获取对象的各种信息。
(2)Java属于先编译再运行的语言
,程序中对象的类型在编译期就确定下来了,而当程序在运行时可能需要动态加载某些类,这些类因为之前用不到,所以没有被加载到JVM。通过反射,可以在运行时动态地创建对象并调用其属性,不需要提前在编译期知道运行的对象是谁。
反射优缺点
1、优点:在运行时获得类的各种内容,进行反编译,对于Java这种先编译再运行的语言,能够让我们很方便的创建灵活的代码,这些代码可以在运行时装配,无需在组件之间进行源代码的链接,更加容易实现面向对象。
2、缺点:反射调用方法时可以忽略权限检查,因此可能会破坏封装性而导致安全问题。
获取类的三种方法
(1)Object–>getClass
Student stu1 = new Student();//这一new 产生一个Student对象,一个Class对象。
Class stuClass = stu1.getClass();//获取Class对象
(2)任何数据类型(包括基本的数据类型)都有一个“静态”的class属性
Class stuClass2 = Student.class;
(3)通过class类的静态方法:forName(String className)(最常用)
其中第三种方法使用居多
try {
Class stuClass3 = Class.forName("fanshe.Student");//注意此字符串必须是真实路径,就是带包名的类路径,包名.类名
System.out.println(stuClass3 == stuClass2);//判断三种方式是否获取的是同一个Class对象
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
反射方法使用-消除泛型检查
import java.lang.reflect.Method;
import java.util.ArrayList;
/*
* 通过反射越过泛型检查
* 例如:有一个String泛型的集合,怎样能向这个集合中添加一个Integer类型的值?
*/
public class Demo {
public static void main(String[] args) throws Exception{
ArrayList<String> strList = new ArrayList<>();
strList.add("aaa");
strList.add("bbb");
// strList.add(100);
//获取ArrayList的Class对象,反向的调用add()方法,添加数据
Class listClass = strList.getClass(); //得到 strList 对象的字节码 对象
//获取add()方法
Method m = listClass.getMethod("add", Object.class);
//调用add()方法
m.invoke(strList, 100);
//遍历集合
for(Object obj : strList){
System.out.println(obj);
}
}
}
输出如下
aaa
bbb
100
代理
静态代理
一、定义:某个对象提供一个代理,代理角色固定,以控制对这个对象的访问。 代理类和委托类有共同的父类或父接口,这样在任何使用委托类对象的地方都可以用代理对象替代。代理类负责请求的预处理、过滤、将请求分派给委托类处理、以及委托类执行完请求后的后续处理。
二、举例
a、有共同的行为(结婚) - 接口
b、目标角色(新人) - 实现行为
c、代理角色(婚庆公司) - 实现行为 增强目标对象行为
某个对象就是新人,代理角色就是婚庆公司。
具体代码
/**
* 定义行为
*/
public interface Marry {
public void toMarry();
}
目标对象(实现行为)
/**
* 静态代理 ——> 目标对象
*/
public class You implements Marry {
// 实现行为
@Override
public void toMarry() {
System.out.println("我要结婚了...");
}
}
代理对象(实现行为、增强目标对象的行为)
定义行为(共同) 定义接口
/**
* 静态代理 ——> 代理对象
*/
public class MarryCompanyProxy implements Marry {
// 目标对象
private Marry marry;
// 通过构造器将目标对象传入
public MarryCompanyProxy(Marry marry) {
this.marry = marry;
}
// 实现行为
@Override
public void toMarry() {
// 增强行为
before();
// 执行目标对象中的方法
marry.toMarry();
// 增强行为
after();
}
/**
* 增强行为
*/
private void after() {
System.out.println("新婚快乐,早生贵子!");
}
/**
* 增强行为
*/
private void before() {
System.out.println("场地正在布置中...");
}
}
通过代理对象实现目标对象的功能
// 目标对象
You you = new You();
// 构造代理角色同时传入真实角色
MarryCompanyProxy marryCompanyProxy = new MarryCompanyProxy(you);
// 通过代理对象调用目标对象中的方法
marryCompanyProxy.toMarry();
动态代理
相比于静态代理,动态代理在创建代理对象上更加的灵活,动态代理类的字节码在程序运行时,由Java反射机制动态产生。它会根据需要,通过反射机制在程序运行期,动态的为目标对象创建代理对象,无需程序员手动编写它的源代码。动态代理不仅简化了编程工作,而且提高了软件系统的可扩展性,因为反射机制可以生成任意类型的动态代理类。代理的行为可以代理多个方法,即满足生产需要的同时又达到代码通用的目的。