Java8,编译内部类调用私有方法,会生成对应私有方法的[static synthetic]方法,这样就会导致代码热更失败

3 篇文章 0 订阅

JDK8编译

  1.  直接访问内部类的成员变量,读取或者写入都会在被访问的内部类中生成public类型的access方法。

  2. 访问内部类的私有方法,也会在被访问类的内部生成public类型的access方法。

  3. 内部的protected、public的方法和成员变量可以直接访问。

public class GameOut {

    private int outId;
    private String outName;

    private InnerA innerA;

    private int getOutId() {
        return outId;
    }

    private void setOutId(int outId) {
        this.outId = outId;
    }

    private String getOutName() {
        return outName;
    }

    private void setOutName(String outName) {
        this.outName = outName;
    }

    private String joinStr(){
        return outId + outName;
    }

    private boolean isOk(String str) {
        return str.length() > outName.length();
    }

    public void outTest() {
        InnerA innerA = new InnerA();
        innerA.setId(1001);
        innerA.setName("innerA");
        innerA.setAge(10);

        printInner(innerA.getId(), innerA.getName(), innerA.getAge());

        innerA.id = 2000;
        innerA.name = "innerA2";
        innerA.age = 20;

        printInner(innerA.id, innerA.name, innerA.age);


        setOutId(1);
        setOutName("gameOut");

        innerA.test();
    }

    private void printInner(int id, String name, int age) {
        System.out.println("id = " + id + ", name = " + name + ", age = " + age);
    }

    private class InnerA {
        private int id;
        String name;
        public int age;

        private int getId() {
            return id;
        }

        private void setId(int id) {
            this.id = id;
        }

        String getName() {
            return name;
        }

        void setName(String name) {
            this.name = name;
        }

        public int getAge() {
            return age;
        }

        public void setAge(int age) {
            this.age = age;
        }

        public void test() {
            System.out.println("getOutId() = " + getOutId());
            System.out.println("getOutName() = " + getOutName());

            final String s = joinStr();
            System.out.println("s = " + s);

            final boolean ok = isOk(s);
            System.out.println("ok = " + ok);
        }
    }
}

生成的GameOut字节码函数列表

outTest函数的字节码

 0 new #58 <GameOut$InnerA>
 3 dup
 4 aload_0
 5 aconst_null
 6 invokespecial #60 <GameOut$InnerA.<init>>
 9 astore_1
10 aload_1
11 sipush 1001
14 invokestatic #63 <GameOut$InnerA.access$1>
17 aload_1
18 ldc #67 <innerA>
20 invokevirtual #68 <GameOut$InnerA.setName>
23 aload_1
24 bipush 10
26 invokevirtual #71 <GameOut$InnerA.setAge>
29 aload_0
30 aload_1
31 invokestatic #74 <GameOut$InnerA.access$2>
34 aload_1
35 invokevirtual #78 <GameOut$InnerA.getName>
38 aload_1
39 invokevirtual #81 <GameOut$InnerA.getAge>
42 invokespecial #84 <GameOut.printInner>
45 aload_1
46 sipush 2000
49 invokestatic #88 <GameOut$InnerA.access$3>
52 aload_1
53 ldc #91 <innerA2>
55 putfield #93 <GameOut$InnerA.name>
58 aload_1
59 bipush 20
61 putfield #96 <GameOut$InnerA.age>
64 aload_0
65 aload_1
66 invokestatic #99 <GameOut$InnerA.access$4>
69 aload_1
70 getfield #93 <GameOut$InnerA.name>
73 aload_1
74 getfield #96 <GameOut$InnerA.age>
77 invokespecial #84 <GameOut.printInner>
80 aload_0
81 iconst_1
82 invokespecial #102 <GameOut.setOutId>
85 aload_0
86 ldc #104 <gameOut>
88 invokespecial #106 <GameOut.setOutName>
91 aload_1
92 invokevirtual #108 <GameOut$InnerA.test>
95 return

GameOut的accesss字节码

// GameOut#access$0
0 aload_0
1 invokespecial #134 <GameOut.getOutId>
4 ireturn

// GameOut#access$1
0 aload_0
1 invokespecial #137 <GameOut.getOutName>
4 areturn


// GameOut#access$2
0 aload_0
1 invokespecial #139 <GameOut.joinStr>
4 areturn

// GameOut#access$3
0 aload_0
1 aload_1
2 invokespecial #142 <GameOut.isOk>
5 ireturn

InnerA的字节码函数列表

InnerA的access方法列表

// access$1
0 aload_0
1 iload_1
2 invokespecial #101 <GameOut$InnerA.setId>
5 return


// access$2
0 aload_0
1 invokespecial #104 <GameOut$InnerA.getId>
4 ireturn

// access$3
0 aload_0
1 iload_1
2 putfield #26 <GameOut$InnerA.id>
5 return

// access$4
0 aload_0
1 getfield #26 <GameOut$InnerA.id>
4 ireturn

InnerA的test方法字节码

  0 getstatic #41 <java/lang/System.out>
  3 new #47 <java/lang/StringBuilder>
  6 dup
  7 ldc #49 <getOutId() = >
  9 invokespecial #51 <java/lang/StringBuilder.<init>>
 12 aload_0
 13 getfield #15 <GameOut$InnerA.this$0>
 16 invokestatic #53 <GameOut.access$0>
 19 invokevirtual #59 <java/lang/StringBuilder.append>
 22 invokevirtual #63 <java/lang/StringBuilder.toString>
 25 invokevirtual #66 <java/io/PrintStream.println>
 28 getstatic #41 <java/lang/System.out>
 31 new #47 <java/lang/StringBuilder>
 34 dup
 35 ldc #71 <getOutName() = >
 37 invokespecial #51 <java/lang/StringBuilder.<init>>
 40 aload_0
 41 getfield #15 <GameOut$InnerA.this$0>
 44 invokestatic #73 <GameOut.access$1>
 47 invokevirtual #77 <java/lang/StringBuilder.append>
 50 invokevirtual #63 <java/lang/StringBuilder.toString>
 53 invokevirtual #66 <java/io/PrintStream.println>
 56 aload_0
 57 getfield #15 <GameOut$InnerA.this$0>
 60 invokestatic #80 <GameOut.access$2>
 63 astore_1
 64 getstatic #41 <java/lang/System.out>
 67 new #47 <java/lang/StringBuilder>
 70 dup
 71 ldc #83 <s = >
 73 invokespecial #51 <java/lang/StringBuilder.<init>>
 76 aload_1
 77 invokevirtual #77 <java/lang/StringBuilder.append>
 80 invokevirtual #63 <java/lang/StringBuilder.toString>
 83 invokevirtual #66 <java/io/PrintStream.println>
 86 aload_0
 87 getfield #15 <GameOut$InnerA.this$0>
 90 aload_1
 91 invokestatic #85 <GameOut.access$3>
 94 istore_2
 95 getstatic #41 <java/lang/System.out>
 98 new #47 <java/lang/StringBuilder>
101 dup
102 ldc #89 <ok = >
104 invokespecial #51 <java/lang/StringBuilder.<init>>
107 iload_2
108 invokevirtual #91 <java/lang/StringBuilder.append>
111 invokevirtual #63 <java/lang/StringBuilder.toString>
114 invokevirtual #66 <java/io/PrintStream.println>
117 return

以上是示例代码。内部类会调用外部类的私有方法,外部类也会调用内部类的私有方法。但是使用java8编译器生成的代码,对于调用的私有方法会生成对应的access$的[static synthetic]方法,实现内部的私有方法访问。这样也会导致热更代码失败。

 

 

上面是内部类和外部类,生成的字节码,函数列表。都会包括很多access方法,用于访问对应的私有方法。

这样,内部类访问的私有变量就会转变为access方法的调用。

注意:jdk8下私有类的私有变量即使存在public方法,但如果使用“.变量”的方式访问,依然会生成一个access方法,影响热更。需要直接调用get方法才能正常热更。

总结:JDK8编译

  1. 内部类的私有方法调用,生成access$开头的[static synthetic]方法,访问级别package。
  2. 内部类访问私有变量,生成对应的get或者set开头的方法,访问级别package。(访问非私有变量,直接getfield访问)
  3. 内部的protected、public的方法和成员变量可以直接访问。

JDK11编译的字节码

GameOut的函数列表

没有access方法,GameOut的outTest方法的字节码

 0 new #58 <GameOut$InnerA>
 3 dup
 4 aload_0
 5 invokespecial #60 <GameOut$InnerA.<init>>
 8 astore_1
 9 aload_1
10 sipush 1001
13 invokevirtual #63 <GameOut$InnerA.setId>
16 aload_1
17 ldc #66 <innerA>
19 invokevirtual #67 <GameOut$InnerA.setName>
22 aload_1
23 bipush 10
25 invokevirtual #70 <GameOut$InnerA.setAge>
28 aload_0
29 aload_1
30 invokevirtual #73 <GameOut$InnerA.getId>
33 aload_1
34 invokevirtual #76 <GameOut$InnerA.getName>
37 aload_1
38 invokevirtual #79 <GameOut$InnerA.getAge>
41 invokevirtual #82 <GameOut.printInner>
44 aload_1
45 sipush 2000
48 putfield #86 <GameOut$InnerA.id>
51 aload_1
52 ldc #89 <innerA2>
54 putfield #91 <GameOut$InnerA.name>
57 aload_1
58 bipush 20
60 putfield #94 <GameOut$InnerA.age>
63 aload_0
64 aload_1
65 getfield #86 <GameOut$InnerA.id>
68 aload_1
69 getfield #91 <GameOut$InnerA.name>
72 aload_1
73 getfield #94 <GameOut$InnerA.age>
76 invokevirtual #82 <GameOut.printInner>
79 aload_0
80 iconst_1
81 invokevirtual #97 <GameOut.setOutId>
84 aload_0
85 ldc #99 <gameOut>
87 invokevirtual #101 <GameOut.setOutName>
90 aload_1
91 invokevirtual #103 <GameOut$InnerA.test>
94 return

JDK11下可以直接访问内部类的私有方法和变量。

InnerA的字节码函数列表

也没有access方法,InnerA的test方法字节码

  0 getstatic #41 <java/lang/System.out>
  3 new #47 <java/lang/StringBuilder>
  6 dup
  7 ldc #49 <getOutId() = >
  9 invokespecial #51 <java/lang/StringBuilder.<init>>
 12 aload_0
 13 getfield #15 <GameOut$InnerA.this$0>
 16 invokevirtual #53 <GameOut.getOutId>
 19 invokevirtual #58 <java/lang/StringBuilder.append>
 22 invokevirtual #62 <java/lang/StringBuilder.toString>
 25 invokevirtual #65 <java/io/PrintStream.println>
 28 getstatic #41 <java/lang/System.out>
 31 new #47 <java/lang/StringBuilder>
 34 dup
 35 ldc #70 <getOutName() = >
 37 invokespecial #51 <java/lang/StringBuilder.<init>>
 40 aload_0
 41 getfield #15 <GameOut$InnerA.this$0>
 44 invokevirtual #72 <GameOut.getOutName>
 47 invokevirtual #75 <java/lang/StringBuilder.append>
 50 invokevirtual #62 <java/lang/StringBuilder.toString>
 53 invokevirtual #65 <java/io/PrintStream.println>
 56 aload_0
 57 getfield #15 <GameOut$InnerA.this$0>
 60 invokevirtual #78 <GameOut.joinStr>
 63 astore_1
 64 getstatic #41 <java/lang/System.out>
 67 new #47 <java/lang/StringBuilder>
 70 dup
 71 ldc #81 <s = >
 73 invokespecial #51 <java/lang/StringBuilder.<init>>
 76 aload_1
 77 invokevirtual #75 <java/lang/StringBuilder.append>
 80 invokevirtual #62 <java/lang/StringBuilder.toString>
 83 invokevirtual #65 <java/io/PrintStream.println>
 86 aload_0
 87 getfield #15 <GameOut$InnerA.this$0>
 90 aload_1
 91 invokevirtual #83 <GameOut.isOk>
 94 istore_2
 95 getstatic #41 <java/lang/System.out>
 98 new #47 <java/lang/StringBuilder>
101 dup
102 ldc #87 <ok = >
104 invokespecial #51 <java/lang/StringBuilder.<init>>
107 iload_2
108 invokevirtual #89 <java/lang/StringBuilder.append>
111 invokevirtual #62 <java/lang/StringBuilder.toString>
114 invokevirtual #65 <java/io/PrintStream.println>
117 return

总结:对于java11编译出来的代码是没有access方法的,可以直接访问私有变量和方法。这么来说java11就不会影响热更。

 

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
数据偏态是指数据分布不均衡的情况,这在机器学习和数据挖掘中非常常见。处理数据偏态的方法有很多,下面介绍几种常用的方法: 1. 过采样:增加数据量少的类别的样本数量,使得数据分布均匀。常用的过采样方法有SMOTE(Synthetic Minority Over-sampling Technique)和ADASYN(Adaptive Synthetic Sampling)。 2. 欠采样:减少数据量多的类别的样本数量,使得数据分布均衡。常用的欠采样方法有Random Under-sampling和NearMiss。 3. 基于代价敏感学习:通过给不同类别赋予不同的代价,来使得模型加关注数据量少的类别。比如在支持向量机(SVM)中,可以通过设置C参数来调整不同类别的代价。 4. 集成学习:将多个分类器的结果进行集成,从而得到好的分类效果。比如在随机森林中,可以通过设置不同类别的权重来调整模型的分类效果。 下面是一个Python代码示例,使用SMOTE方法对数据进行过采样: ```python from imblearn.over_sampling import SMOTE from sklearn.datasets import make_classification # 生成不均衡数据集 X, y = make_classification(n_classes=2, class_sep=2, weights=[0.1, 0.9], n_informative=3, n_redundant=1, flip_y=0, n_features=20, n_clusters_per_class=1, n_samples=1000, random_state=10) # 过采样 sm = SMOTE(random_state=42) X_res, y_res = sm.fit_resample(X, y) # 查看数据分布 import pandas as pd pd.Series(y_res).value_counts() ``` 输出结果如下: ``` 1 900 0 900 dtype: int64 ``` 可以看到,经过过采样后,数据分布已经变得加均衡了。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值