Java安全基础 关键概念汇总
文章目录
- Java安全基础 关键概念汇总
- 前置知识
- 1.构造器this以及包的使用
- 2.继承
- 3.重写/ 重载 / super
- 4.多态
- 5.区分`==`和`equals`方法
- 6.toString的使用
- 7.Object的概念
- 8.static,final,代码块
- static
- 代码块
- final
- 9.动态代理
- 10.类的动态加载
- 1)类加载器含义(类的底层实现)
- 2)ClassLoader
- 3)类加载器的核心方法
- 4)**双亲委派模型过程**(强调逻辑关系不是继承关系)
- 5)**双亲委派模型的系统实现**
- 6)ClassLoader(类加载机制)
- 7)类加载流程(关注加载后是否初始化)
- 8)动态类的加载方法
- 安全关注:实现加载任意类(字节码)
前置知识
了解Java基本面向对象语法以及反射
1.构造器this以及包的使用
假设在Person类中
this.name=name
this指代Person类的对象等价于Person.name=name
就是 当前类的引用
应用:主要作用就是区分类中的成员属性和变量(比如同名时进行区分)
包中使用遵循见包起名import
导入
2.继承
关键字extends
经典的父子关系,子类可以继承父类的public
成员属性和成员方法
但私有的private
无法继承
子类可以在父类的基础上有自己的特性
3.重写/ 重载 / super
重写概念:子类继承父类后,对同名同参数的方法进行覆盖
与重载区分开:重载是同名跟紧不同参数执行不同方法(注意几个参数)
关键词super
可以直接调用父类的构造方法,成员变量,成员方法
应用:一般多用于 重写后 区分子类和父类方法以及属性(同名)
4.多态
对象的多态性核心:父类的指针指向子类对象
例如 我们创建了父类Animal 子类Dog Dog继承了Animal
典型例子:Animal dog=new Dog();
的代码
实际调用的是 父类中声明过的 子类方法和属性
**父类的指针指向子类对象 **换句话说就是 具体实现的类还是子类new Dog()
不过声明 对象时 为 缩小的能力声明
多态性
- 变量类型声明只是调用能力声明;
- 子类型对象的能力一定不小于父类型对象的能力;
- 真正的行为表现要看其具体对象的类型,而不是看引用变量的类型。
5.区分==
和equals
方法
equals
:比较两个内存地址值是否相同
Dog dog1=new Dog(); //在new时分配对象产生内存空间
Dog dog2=new Dog();
dog1.equals(dog2)//返回False
对象具体的分配内存空间不同
==
:判断具体的值是否相同
6.toString的使用
输出 引用对象,自动调用类中的toString方法
7.Object的概念
Java中所有对象的根父类
8.static,final,代码块
static
不用初始化创建对象 就可以调用成员方法和属性类名.属性 //类名. 方法名()
代码块
1.代码块{}
2.静态代码块statci{}
3.构造器Dog(){}
调用顺序 静态代码块---->代码块----->构造器
为什么是这个调用顺序了?
详见10.类的动态加载
final
可以理解为 最后 的修改
final 修饰 类 类不可被调用
final 修饰 方法 方法不可被重写
final 修饰变量 变量不可被修改
当然通过反射也是可以修改的
9.动态代理
参考:https://www.liaoxuefeng.com/wiki/1252599548343744/1264804593397984
回顾一下
Java的class
和interface
的区别:
-
可以实例化
class
(非abstract
); -
不能实例化
interface
。
代理含义:通过继承和多态实现间接调用
动态代码:运行期动态创建某个interface
的实例,没有实现类但是在运行期动态创建了一个接口对象的方式
动态代理具体实现过程
-
定义一个
InvocationHandler
实例,它负责实现接口的方法调用(具体实现); -
通过
Proxy.newProxyInstance()
创建接口实例
interface
它需要3个参数:
- 使用的
ClassLoader
,通常就是接口类的ClassLoader
; - 需要实现的接口数组,至少需要传入一个接口进去;
- 用来处理接口方法调用的
InvocationHandler
实例。
- 使用的
-
将返回的
Object
强制转型为接口。
例子
public class Main {
public static void main(String[] args) {
InvocationHandler handler = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println(method);
if (method.getName().equals("morning")) {
System.out.println("Good morning, " + args[0]);
}
return null;
}
};
Hello hello = (Hello) Proxy.newProxyInstance(
Hello.class.getClassLoader(), // 传入ClassLoader
new Class[] { Hello.class }, // 传入要实现的接口
handler); // 传入处理调用方法的InvocationHandler
hello.morning("Bob");
}
}
interface Hello {
void morning(String name);
}
实现 代理.方法名
传递方法名 到具体实现类调用方法名
应用:无危害类的.abc方法----->无危害类代理危害类----->触发 危害类.abc方法
非开发预期的危险调用 CC1链
10.类的动态加载
1)类加载器含义(类的底层实现)
类加载器(ClassLoader)是Java语言的一项创新,也是Java流行的一个重要原因。在类加载的第一阶段“加载”过程中,需要通过一个类的全限定名来获取定义此类的二进制字节流,完成这个动作的代码块就是类加载器。这一动作是放在Java虚拟机外部去实现的,以便让应用程序自己决定如何获取所需的类
类加载器的开放性使得Java应用程序可以灵活地从不同的来源获取类的二进制字节流(字节码),包括从ZIP包、网络、运行时计算、其他文件
启动类加载器(Bootstrap ClassLoader):
这个类加载器负责将\lib目录下的类库加载到虚拟机内存中,用来加载java的核心库,此类加载器并不继承于java.lang.ClassLoader,不能被java程序直接调用,代码是使用C++编写的.是虚拟机自身的一部分.
扩展类加载器(Extendsion ClassLoader):
这个类加载器负责加载\lib\ext目录下的类库,用来加载java的扩展库,开发者可以直接使用这个类加载器.
应用程序类加载器(Application ClassLoader):
这个类加载器负责加载用户类路径(CLASSPATH)下的类库,一般我们编写的java类都是由这个类加载器加载,这个类加载器是CLassLoader中的getSystemClassLoader()方法的返回值,所以也称为系统类加载器.一般情况下这就是系统默认的类加载器.
除此之外,我们还可以加入自己定义的类加载器,以满足特殊的需求,需要继承java.lang.ClassLoader类
2)ClassLoader
所有的Java类都必须经过JVM加载后才能运行,ClassLoader
的主要作用就是Java类文件的加载。在JVM类加载器中最顶层的是Bootstrap ClassLoader(引导类加载器)
、Extension ClassLoader(扩展类加载器)
、App ClassLoader(系统类加载器)
,AppClassLoader
是默认的类加载器,如果类加载时我们不指定类加载器的情况下
默认会使用AppClassLoader
加载类,
ClassLoader.getSystemClassLoader()
返回的系统类加载器也是AppClassLoader
。
某些时候我们获取一个类的类加载器时候可能会返回一个null
值,如:java.io.File.class.getClassLoader()
将返回一个null
对象,因为java.io.File
类在JVM初始化的时候会被Bootstrap ClassLoader(引导类加载器)
加载(该类加载器实现于JVM层,采用C++编写),我们在尝试获取被Bootstrap ClassLoader
类加载器所加载的类的ClassLoader
时候都会返回null
3)类加载器的核心方法
loadClass
(加载指定的Java类)findClass
(查找指定的Java类)findLoadedClass
(查找JVM已经加载过的类)defineClass
(定义一个Java类)resolveClass
(链接指定的Java类)
4)双亲委派模型过程(强调逻辑关系不是继承关系)
双亲委派模型的工作过程为:如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,每一个层次的加载器都是如此,因此所有的类加载请求都会传给顶层的启动类加载器,只有当父加载器反馈自己无法完成该加载请求(该加载器的搜索范围中没有找到对应的类)时,子加载器才会尝试自己去加载。
代码还会偷懒,不重复加载(向上寻找想偷懒)
使用双亲委派模型的好处在于Java类随着它的类加载器一起具备了一种带有优先级的层次关系。例如类java.lang.Object,它存在在rt.jar中,无论哪一个类加载器要加载这个类
因此带有优先级的层次关系 通过查询路径反应
最终都是委派给处于模型最顶端的Bootstrap ClassLoader(由Java实现C++编写,尝试获取被Bootstrap ClassLoader
类加载器所加载的类附属性 的ClassLoader
时候都会返回null
)进行加载 就是 副属性 parent
为null
因此Object类在程序的各种类加载器环境中都是同一个类。相反,如果没有双亲委派模型而是由各个类加载器自行加载的话,如果用户编写了一个java.lang.Object的同名类并放在ClassPath中,那系统中将会出现多个不同的Object类,程序将混乱。因此,如果开发者尝试编写一个与rt.jar类库中重名的Java类,可以正常编译,但是永远无法被加载运行。
5)双亲委派模型的系统实现
在java.lang.ClassLoader的loadClass()方法中,先检查是否已经被加载过,若没有加载则调用父类加载器的loadClass()方法,若父加载器为空则默认使用启动类加载器作为父加载器。如果父加载失败,则抛出ClassNotFoundException异常后,再调用自己的findClass()方法进行加载。
6)ClassLoader(类加载机制)
Java程序在运行前需要先编译成class文件
,Java类初始化的时候会调用java.lang.ClassLoader
加载类字节码,ClassLoader
会调用JVM的native方法(defineClass0/1/2
)来定义一个java.lang.Class
实例。
7)类加载流程(关注加载后是否初始化)
让我们复习一下代码块
初始化
1.代码块{}
2.静态代码块statci{}
任何静态相关的操作自动调用
创建实例(过程中包括初始化)
3.构造器Dog(){}
解释为什是这个调用顺序 静态代码块---->代码块----->构造器
static {} 就是在“类初始化”的时候调⽤的,⽽ {} 中的代码会放在构造函数的 super() 后⾯,但在当前构造函数内容的前⾯
8)动态类的加载方法
1.Class.forName("类名")
默认会初始化被加载类的静态属性和方法
2.ClassLoader.loadClass
默认不会初始化类方法
安全关注:实现加载任意类(字节码)
逻辑上EXT和APP关系如上图,但是继承上同级,都返回父类URLClassLoader查询
URLClassloader : 支持 file://,jar://,http://
加载字节码方式(实例化)
1.通过反射加载defineClass(protected)
2.Unsafe加载字节码
方法为public,但是无法取unsafe,通过反射静态属性拿unsafe实例