java pkcs10,话说 类加载过程 第一篇

1. 类加载初始化

Loading

把一个class文件加载到内存

Linking

Verification

校验class文件符不符合class文件标准

Preparation

静态变量赋默认值 static int count = 10;

在这一步count = 0 默认值

Resolution

符号引用转换为内存地址 可以直接访问的地址

Initializing

静态变量赋值为初始值

1c11d0c22a91e029af789fd60c7b5bfd.png

2. 类加载器

1. JVM是按需动态加载采用双亲委派机制

用getClassLoader获取类加载器 如果是Null 那就是到了BootStrap类加载器 了 因为是C++实现的 木有对应类

我们平时写的类 就再classpath下 所以是由App类加载器加载的

注意: 这四个类加载器没有继承关系 只是顺序关系

​ 父加载器不是父类,只是说自己加载不了了,就交给上一级加载

​ 他们不是继承关系 那是怎么产生联系? 用组合啦~~

5ddef74df3e79ac22f941bbb39376e6a.png

2. 步骤1图是不是不能说明双亲委派? 我给你一张图 让你明白一下双亲委派

不 ! 我为什么要用图? 我先用一句话描述:classLoader在自己的cache缓存中找是不是已经加载过类XX,如果没有就找他的父类加载器,父类加载器在自己的cache缓存中找是不是已经加载过类XX,找到就返回,找不到就接着找父类加载器,直到BootStrap 如果最后还没在cache中找到 那就从父类加载器往回找,父加载器问儿子你看看你负责的jar包什么的 有没有这个类,有的话你就加载到内存,没有的话你就找你儿子类加载器去看,一直看看看看,看到最后如果有返回最小的类加载器,还是没找见 那就抛异常吧 ClassNotFoundException

​ 我还是屈服了,上图吧。

81f416bc846abea6dbf782bd1dde1f78.png

/**

* @author 木子的昼夜

*/

public class MyClassLoaderTest {

public static void main(String[] args) throws ClassNotFoundException {

MyClassLoaderTest.class.getClassLoader().loadClass("classtest.MyClassLoaderTest");

}

}

// ClassLoader#loadClass

public Class> loadClass(String name) throws ClassNotFoundException {

return loadClass(name, false);

}

protected Class> loadClass(String name, boolean resolve)

throws ClassNotFoundException

{

// 肯定是要上锁的 不然加载冲了咋个办呢?

synchronized (getClassLoadingLock(name)) {

// First, check if the class has already been loaded

// 首先,判断这个类是否已经加载到当前层级内存了

Class> c = findLoadedClass(name);

// 如果当前层级内存没有

if (c == null) {

long t0 = System.nanoTime();

try {

if (parent != null) {

// 如果还有父级 那就在上一层的缓存里找

c = parent.loadClass(name, false);

} else {

// 如果没有了 那就是最后一层了 在自己的管辖范围 找

c = findBootstrapClassOrNull(name);

}

} catch (ClassNotFoundException e) {

// ClassNotFoundException thrown if class not found

// from the non-null parent class loader

}

// 上级一直没有找到 那就只能找自己负责的范围了

if (c == null) {

// If still not found, then invoke findClass in order

// to find the class.

// 如果还是没有 那就往下一级 接着在负责范围找

long t1 = System.nanoTime();

c = findClass(name);

// this is the defining class loader; record the stats

sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);

sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);

sun.misc.PerfCounter.getFindClasses().increment();

}

}

if (resolve) {

resolveClass(c);

}

return c;

}

}

设计模式:钩子函数 模板方法

3. 一个class文件被load到内存里是什么样

1eb7ede816a9e277cf15f0c617737ecd.png

4. 想知道某个类是被哪个类加载器加载到内存里的 getClassLoader()

System.out.println(String.class.getClassLoader());

// null --> BootStrap类加载器

System.out.println(sun.awt.HKSCS.class.getClassLoader());

// null --> BootStrap类加载器

System.out.println(sun.net.spi.nameservice.dns.DNSNameService.class.getClassLoader());

// sun.misc.Launcher$ExtClassLoader@1d44bcfa --> Ext

System.out.println(ClassLoaderTest.class.getClassLoader());

// sun.misc.Launcher$AppClassLoader@18b4aac2--> App

5. 为啥搞双亲委派

为了安全 如果没有双亲委派 直接从自定义类加载器找class (主要问题)

3697cd48070d24603e9a77b7f763778c.png

​ (1) 如果这里我自定义一个java.lang.String 我在String类里边写一段挖矿脚本

​ (2) 然后小月月下载了我的类库 准备使用 !

​ 他用到了String ,他以为这是jdk的类,结果我预判了他的预判,这个String类已经是我的了,由我掌控

​ (3) 双亲委派就可以避免这个问题,因为系统始终会先去上一层看有没有这个类 因为上一层有了 所以就不 会使用我定义的String类了

如果父层加载了 我就不用加载了 再加载就是浪费资源了 (次要问题)

6. 既然自定义了 不是我说什么就是什么吗 ?

​ 不是! 类加载器已经限制死了

7. 类加载器父子关系示例 不是继承 是组合

看下边代码,读者可以一试 再仔细品一品 这句话: 加载类加载器的类加载器 不是类加载器的父类加载器

System.out.println(ParentDemo.class.getClassLoader());

System.out.println(ParentDemo.class.getClassLoader().getClass().getClassLoader());

System.out.println(ParentDemo.class.getClassLoader().getParent());

System.out.println(ParentDemo.class.getClassLoader().getParent().getParent());

输出结果:

sun.misc.Launcher$AppClassLoader@18b4aac2

null

sun.misc.Launcher$ExtClassLoader@6e0be858

null

8. 各个类加载器范围

可以看一眼 sun.misc.Launcher 源码

BootstrapClassLoader : sun.boot.class.path

ExtensionClassLoader: java.ext.dirs

AppClassLoader:java.class.path

public static void main(String[] args) {

System.out.println("---------BOOT-----------------------");

String boot = System.getProperty("sun.boot.class.path");

Arrays.stream(boot.split(";")).forEach(s-> System.out.println(s));

System.out.println("--------------Ext-------------------");

String ext = System.getProperty("java.ext.dirs");

Arrays.stream(ext.split(";")).forEach(s-> System.out.println(s));

System.out.println("----------APP-----------------------");

String app = System.getProperty("java.class.path");

Arrays.stream(app.split(";")).forEach(s-> System.out.println(s));

}

输出结果:

---------BOOT-----------------------

C:\Program Files\Java\jdk1.8.0_144\jre\lib\resources.jar

C:\Program Files\Java\jdk1.8.0_144\jre\lib\rt.jar

C:\Program Files\Java\jdk1.8.0_144\jre\lib\sunrsasign.jar

C:\Program Files\Java\jdk1.8.0_144\jre\lib\jsse.jar

C:\Program Files\Java\jdk1.8.0_144\jre\lib\jce.jar

C:\Program Files\Java\jdk1.8.0_144\jre\lib\charsets.jar

C:\Program Files\Java\jdk1.8.0_144\jre\lib\jfr.jar

C:\Program Files\Java\jdk1.8.0_144\jre\classes

--------------Ext-------------------

C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext

C:\WINDOWS\Sun\Java\lib\ext

----------APP-----------------------

C:\Program Files\Java\jdk1.8.0_144\jre\lib\charsets.jar

C:\Program Files\Java\jdk1.8.0_144\jre\lib\deploy.jar

C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\access-bridge-64.jar

C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\cldrdata.jar

C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\dnsns.jar

C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\jaccess.jar

C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\jfxrt.jar

C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\localedata.jar

C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\nashorn.jar

C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\sunec.jar

C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\sunjce_provider.jar

C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\sunmscapi.jar

C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\sunpkcs11.jar

C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\zipfs.jar

C:\Program Files\Java\jdk1.8.0_144\jre\lib\javaws.jar

C:\Program Files\Java\jdk1.8.0_144\jre\lib\jce.jar

C:\Program Files\Java\jdk1.8.0_144\jre\lib\jfr.jar

C:\Program Files\Java\jdk1.8.0_144\jre\lib\jfxswt.jar

C:\Program Files\Java\jdk1.8.0_144\jre\lib\jsse.jar

C:\Program Files\Java\jdk1.8.0_144\jre\lib\management-agent.jar

C:\Program Files\Java\jdk1.8.0_144\jre\lib\plugin.jar

C:\Program Files\Java\jdk1.8.0_144\jre\lib\resources.jar

C:\Program Files\Java\jdk1.8.0_144\jre\lib\rt.jar

E:\workspace_idea\blogmaven\target\classes

// 读者注意看这一个 这个是当前项目class文件地址

9. 自定义类加载器

先定一个一个被加载的java类 Test.java public class Test {

public void say(){

System.out.println("成功喽!");

}

}

编译Test.java 到指定目录 javac -encoding utf-8 -d E:/classes Test.java

继承ClassLoader 、 重写模板方法findClass 、调用defineClass /**

* @author 木子的昼夜

*/

public class MyClassLoader extends ClassLoader {

@Override

protected Class> findClass(String name) throws ClassNotFoundException {

FileInputStream inputStream = null;

ByteArrayOutputStream out = null;

try {

// 类名 aa.xx 转换为 aa/xx.class

File file = new File("E:\\classes\\",name.replaceAll("\\.","/").concat(".class"));

// 获取输入流

inputStream = new FileInputStream(file);

// 输出字节流

out = new ByteArrayOutputStream();

// 转换为字节数组

int available = inputStream.available();

byte[] byteArr = new byte[available];

inputStream.read(byteArr);

// 生成class

return defineClass(name,byteArr,0,byteArr.length);

}catch (Exception e) {

throw new ClassNotFoundException();

}finally {

// 关闭流

if (inputStream != null) {

try {

inputStream.close();

} catch (IOException e) {

e.printStackTrace();

}

}

if (out != null) {

try {

out.close();

} catch (IOException e) {

e.printStackTrace();

}

}

}

}

}

测试 public static void main(String[] args) {

try {

Class> clazz = new MyClassLoader().loadClass("Test");

// 获取声明的方法

Method say = clazz.getDeclaredMethod("say");

// 创建实例

Object instance = clazz.newInstance();

// 调用方法

say.invoke(instance);

}catch (Exception e) {

e.printStackTrace();

}

}

输出结果:

成功喽!

自定义类加载器加载自加密的class

文件是你自己的,你在编译完了之后可以通过某种算法加密一下, 等你用自定义ClassLoader加载的时候,你可以自己个儿解密

可以防止反编译

可以防止被篡改

10. 混合编译

解释器 : bytecode intepreter

JIT: Just In - Time compiler 1. -Xmixed 默认为混合模式

开始使用解释执行 启动速度快

对热点代码进行实时检测和编译

2. -Xint 使用解释模式 启动快 执行慢

3. -Xcomp 使用纯编译模式 启动慢 运行快

混合模式

解释器+热点代码编译

起始阶段采用解释器

热点代码检测 检测到了使用热点代码编译 1. 多次被调用的方法(方法计数器:监测方法执行频率)

2. 多次被调用的循环(循环计数器:监测循环执行频率)

未完 待续...

本文有很多图,如果不清楚的话,大家可以关注公众号:木子的昼夜

​ 发送"类加载" 即可获得高清图访问地址

​ 发送"路线" 即可获得本系列文章大纲

​ 也可发送自己想问的问题给我,我会在看到的第一时间回复

最后附上自己公众号刚开始写 愿一起进步:

!

注意: 以上文字 仅代表个人观点,仅供参考,如有问题还请指出,立即马上连滚带爬的从被窝里出来改正。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
PKCS #1(Public-Key Cryptography Standards)是一组密码学标准,用于 RSA 加密和数字签名。PKCS #1 定义了 RSA 的加密、解密、签名和验证算法,并规定了公钥和私钥的格式。 在 Java 中,可以使用 PKCS #1 标准实现 RSA 加密和数字签名。Java 提供了 javax.crypto 包,其中包含了实现 PKCS #1 的类。具体来,可以使用以下类: - javax.crypto.Cipher:用于加密和解密数据。 - java.security.KeyPair:由公钥和私钥组成的密钥对。 - java.security.KeyPairGenerator:用于生成密钥对。 - java.security.KeyFactory:用于从密钥规范中构建密钥。 - java.security.Signature:用于数字签名和验证。 要使用 PKCS #1 实现 RSA 加密,可以按照以下步骤进行: 1. 生成密钥对 ``` KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA"); keyPairGenerator.initialize(2048); KeyPair keyPair = keyPairGenerator.generateKeyPair(); ``` 2. 获取公钥和私钥 ``` PublicKey publicKey = keyPair.getPublic(); PrivateKey privateKey = keyPair.getPrivate(); ``` 3. 加密数据 ``` Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding"); cipher.init(Cipher.ENCRYPT_MODE, publicKey); byte[] encryptedData = cipher.doFinal(data); ``` 4. 解密数据 ``` cipher.init(Cipher.DECRYPT_MODE, privateKey); byte[] decryptedData = cipher.doFinal(encryptedData); ``` 要使用 PKCS #1 实现数字签名和验证,可以按照以下步骤进行: 1. 获取私钥 ``` PrivateKey privateKey = getPrivateKey(); ``` 2. 对数据进行签名 ``` Signature signature = Signature.getInstance("SHA256withRSA"); signature.initSign(privateKey); signature.update(data); byte[] signatureBytes = signature.sign(); ``` 3. 获取公钥 ``` PublicKey publicKey = getPublicKey(); ``` 4. 验证签名 ``` Signature signature = Signature.getInstance("SHA256withRSA"); signature.initVerify(publicKey); signature.update(data); boolean verified = signature.verify(signatureBytes); ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值