Skip to content
Ider

沉淀我所学习,累积我所见闻,分享我所体验

Primary Navigation Menu
Menu
  • Home
  • About Ider
    • Who Ider?
    • Why Ider?
    • How Ider?
    • Where Ider?
    • What Ider?

Keyword

2018-06-29
29 June
On June 29, 2018
In Data Structures(数据结构), Design Patterns(设计模式), Language Tips(语言初试)

隐秘而诡异的Java合成方法

Java程序里其实有很多我们看不到的代码,这些代码由Java编译器在编译过程中生成帮助程序更准确地运行。本文就来深入了解一下由编译器加入到Java代码中的方法(Method),特别是合成方法(Synthetic Method)。

合成方法

合成成员(Synthetic Member)在JVM细则里可以找到简单的定义

A class member that does not appear in the source code must be marked using a Synthetic attribute, or else it must have its ACC_SYNTHETIC flag set.

合成成员包含有合成类(Synthetic Class),合成变量(Synthetic Variable),合成方法(Synthetic Method),本文主要讨论合成方法。

合成方法不出现在.java文件中,不过编译之后会在.class里出现,并可以通过以下几个方法发现它们:

  • 在设置断点进行debug时会在方法栈上看到这些由编译器加入的方法
  • 通过反射(Reflection)查找到方法,通过isSyntehtic()方法来判定是否是合成函数
  • 利用一些反编译工具,比如 javap、JD-GUI、jad,查看.class文件可以找到这些方法

对于一般的Java程序而言并没有什么大的问题,只不过是额外的方法调用,代价比较低。但是像Android,它的Dex文件对于Java的方法有一定的约束,就值得去考虑如何避免额外的方法被生成。下边就来介绍一些常见的会出现合成方法的情况让我们有清楚地了解Java的运行机制。

嵌套类和私有成员

先来看一个代码案例,代码中定义了一个顶层类和嵌套类,互相会方法对方的一些private修饰的方法和字段。

[codesyntax lang=”java” lines=”normal”]

public class Outer {

  private int privateField = 21;
  private Inner inner = new Inner();

  private int privateMethod() {
    return inner.privateMethod();
  }

  private void print() {
    inner.print();
  }

  private static int privateStaticMethod() {
    return 7;
  }

  class Inner {
    private int privateMethod() {
      return privateField;
    }

    private void print() {
      System.out.println(Outer.this.privateMethod());
      System.out.println(privateStaticMethod());
    }
  }

  public static void main(String[] args) {
    Outer outer = new Outer();
    outer.print();
  }
}

[/codesyntax]

之前的文章有介绍了Java的修饰访问符的限制范围,类中由private修饰的成员,只能被该类自身所访问,因此嵌套类的私有成员不应该能被外部类访问。另一方面嵌套类虽然在外部类里面,但其实并不属于外部类的一部分,比如编译上边的代码会得到两个.class文件,Inner类被编译成了package可见的独立类。


Read More →

2016-05-03
03 May
On May 3, 2016
In Design Patterns(设计模式), Knowledge Base(心得笔库), Language Tips(语言初试), Software Engineering(软件工程)

Java的static关键字

已经看过了万能的final关键字在Java中的不同用法和效果,其实static关键字的的用处也很广泛。它的用法更多涉及到面向对象设计(Object-Oriented Design)的思想,有时更加让人迷惑。许多是否应该使用static关键字的决定也常常基于工作经验中形成的约束和规范。本文就来帮助大家解锁static的正确使用姿势,写出更加清晰的代码。

为了更好的了解Java程序,就需要了解代码被编译后的样子,所以下边会涉及到javac和javap这些指令的使用,不用担心都是比较简单直接的用法。也需要借助一些反编译(decompile)工具来查看.class文件,这里用到的是JD-GUI。

  • 静态变量 Static Variable
  • 静态方法 Static Method
  • 静态块 Static Block
  • 静态嵌套类 Static Nested Class
  • 静态引入 Static Import

Static Variable

静态变量(Static Variable)在Java中只有静态成员变量,不同于C++、PHP等,它不允许在方法内定义局部静态变量。静态成员变量也称为类变量,相对的是对象成员变量。这些变量是属于定义的类,而非某个对象实例,但是该类的所有实例都可分享该变量。

用个简单的例子会比较直观一些:
[codesyntax lang=”java” lines=”normal”]

package com.iderzheng.statickeyword;

public class StaticField {
    public static int sField;
    public int mField;

    public void print() {
        System.out.println(
                String.format("%s{sField: %d, mField: %d}", this, sField, mField)
        );
    }

    public static void main(String[] args) {
        StaticField one = new StaticField();
        StaticField two = new StaticField();

        one.sField = 7;
        one.mField = 7;

        two.sField = 21;
        two.mField = 21;

        one.print();
        two.print();

        StaticField.sField = 49;
        // Error: non-static variable mField cannot be referenced from a static context
        // StaticField.mField = 49;
        one.print();
        two.print();
    }
}

[/codesyntax]

编译运行这段代码得到如下结果:

com.iderzheng.statickeyword.StaticField@34469729{sField: 21, mField: 7}
com.iderzheng.statickeyword.StaticField@6d172f8f{sField: 21, mField: 21}
com.iderzheng.statickeyword.StaticField@34469729{sField: 49, mField: 7}
com.iderzheng.statickeyword.StaticField@6d172f8f{sField: 49, mField: 21}

很清楚的看到变量two对sField的修改影响了one中的sField值。而mField在两个对象中是独立互补影响的。也看到可以用类的名字来引用静态变量,这是提倡调用方法,不应该直接用对象变量来引用静态变量,这样做不明确调用的是静态变量,很容易带来副作用。

Static Final Variable

一般在程序中不常定义静态变量,因为静态变量的可被访问和使用的地方比局部变量和对象变量要多,维护起来更加困难。更多时候是跟final结合起来定义常量,对于无法修改的只读变量,就不用担心维护问题了。

对象变量也可以用final修饰定义为常量,只不过每个对象的常量只属于对象自己,而且每个对象都需要分配内存给这些常量。而属于类的静态变量则只需要一份内存甚至无需存储变量的值。所以如果对象变量在每个对象中都是一致的,那么就可以考虑转为静态常量。转成静态常量也能方便地用类名直接进行访问,省去创建多余的中间对象。

Java编译器对于常量会做优化,在引用它们的地方会将它们的值直接嵌入,这样就不需要内存来长期存储这些值,更不要从内存地址中寻找,所以效率要高很多。

还是做一个简单的例子:

[codesyntax lang=”java” lines=”normal”]

package com.iderzheng.statickeyword;

public class StaticFinalFields {

    static final int CONSTANT_INT = 7;
    static final String CONSTANT_STRING = "iderzheng.com";
    static final int[] CONSTANT_ARRAY = { 7, 21, 31 };

    final String name;
    final int constantInt = 21;
    final boolean constantBoolean;

    {
        constantBoolean = true;
    }

    public StaticFinalFields(String name) {
        this.name = name;
    }

    public static void main(String[] args) {
        StaticFinalFields finalFields = new StaticFinalFields("Ider");
        System.out.println(finalFields.name);
        System.out.println(finalFields.constantInt);
        System.out.println(finalFields.constantBoolean);

        System.out.println(CONSTANT_INT);
        System.out.println(CONSTANT_STRING);
        System.out.println(CONSTANT_ARRAY);
    }
}

[/codesyntax]

对于上边的代码,通过javac进行编译后得到StaticFinalFields.class,再讲其通过JD-UI打开,会看到编译后代码的
static-final-inline

[codesyntax lang=”java” lines=”normal”]

package com.iderzheng.statickeyword;

import java.io.PrintStream;

public class StaticFinalFields
{
    static final int CONSTANT_INT = 7;
    static final String CONSTANT_STRING = "iderzheng.com";
    static final int[] CONSTANT_ARRAY = { 7, 21, 31 };
    final String name;
    final int constantInt = 21;

    final boolean constantBoolean = true;

    public StaticFinalFields(String paramString)
    {
        this.name = paramString;
    }

    public static void main(String[] paramArrayOfString) {
        StaticFinalFields localStaticFinalFields = new StaticFinalFields("Ider");
        System.out.println(localStaticFinalFields.name);
        localStaticFinalFields.getClass(); System.out.println(21);
        System.out.println(localStaticFinalFields.constantBoolean);

        System.out.println(7);
        System.out.println("iderzheng.com");
        System.out.println(CONSTANT_ARRAY);
    }
}

[/codesyntax]

从编译后的代码可以看出对于原始类型的变量,在用final进行修饰并内嵌初始化后,使用这些变量的地方就直接用这些值进行了替换。虽然也有非static修饰的对象常量被智能优化后直接内嵌,但是为了代码的维护性,依然应该显示地申明成static。

至于对象变量,它们并非真正意义上的常量,对象的创建后的地址也总会不同,而且对象的内容依然可以被修改,所以这些变量加上static final也无法用其值在使用的地方进行替换。

另外还看到通过block进行初始化的对象变量虽然也是常量,但是因为放入了block,编译器觉得它的初始化比较复杂,所以就没有将其引用直接替换。

直接内嵌常量的可以提高运行的效率,但是也有一些弊端:比如项目A中使用了项目B中的静态常量,成功编译后的A会直接使用那些常量;要是B的常量值被人修改了之后只编译了B而没有重新编译A,那A就不会得到新的值。好在现代的编译器都很智能,可以发现这些依赖关系并正确编译。
Read More →

2016-02-23
23 February
On February 23, 2016
In Design Patterns(设计模式), Knowledge Base(心得笔库), Language Tips(语言初试), Special Tricks(奇技妙招)

Java的final关键字

在《Java关键字分类解析》里介绍了所有Java关键字和保留字的基本用途,到《Java修饰符及其作用对象》又介绍了所有Java修饰符的应用。通过修饰符的表格看到final是唯一一个可以在类(Class)、方法Method、变量(Variable)上都能应用的,但前文也提到final在这些作用对象上的效果是不太一样的,本文就详细展开介绍一下final的具体效果。

final class

当一个类被定义成final class,表示该类的不能被其他类继承,即不能用在extends之后。否则在编译期间就会得到错误。
[codesyntax lang=”java” lines=”normal”]

package com.iderzheng.finalkeyword;

public final class FinalClass {
}

// Error: cannot inherit from final
class PackageClass extends FinalClass {
}

[/codesyntax]
Java支持把class定义成final,似乎违背了面向对象编程的基本原则,但在另一方面,封闭的类也保证了该类的所有方法都是固定不变的,不会有子类的覆盖方法需要去动态加载。这给编译器做优化时提供了更多的可能,最好的例子是String,它就是final类,Java编译器就可以把字符串常量(那些包含在双引号中的内容)直接变成String对象,同时对运算符+的操作直接优化成新的常量,因为final修饰保证了不会有子类对拼接操作返回不同的值。

对于所有不同的类定义—顶层类(全局或包可见)、嵌套类(内部类或静态嵌套类)都可以用final来修饰。但是一般来说final多用来修饰在被定义成全局(public)的类上,因为对于非全局类,访问修饰符已经将他们限制了它们的也可见性,想要继承这些类已经很困难,就不用再加一层final限制。

另外要提到的是匿名类(Anonymous Class)虽然说同样不能被继承,但它们并没有被编译器限制成final。
[codesyntax lang=”java” lines=”normal”]

import java.lang.reflect.Modifier;

public class Main {

    public static void main(String[] args) {
        Runnable anonymous = new Runnable() {
            @Override
            public void run() {
            }
        };

        System.out.println(Modifier.isFinal(anonymous.getClass().getModifiers()));
    }
}

// Output:
// false

[/codesyntax]
Read More →

2015-10-21
21 October
On October 21, 2015
In Knowledge Base(心得笔库), Language Tips(语言初试)

Java关键字分类解析

在Java里有50个关键字和3个保留字,它们不仅限定开发者不能使用这些来作为代码中的标识(这些标识指包(package)名、类(class, interface, enum)名、方法(method)名、变量(variable, field, parameter)名),同时它们对代码也有控制作用。本文就对这些关键字做一些归类分组,让大家更好的了解它们的作用。

Group Category Keyword
类型
Types
原始类型
Primitive Type
boolean
byte
char
double
float
int
long
short
void
类相关
Class Type
class
enum
interface
特殊引用
Special Reference
super
this
结构
Structure
类结构
Class Struture
extends
implements
instanceof
new
包相关
Package
import
package
修饰符
Modifier
访问修饰符
Access Modifier
private
protected
public
基础修饰符
Basic Modifier
abstract
final
native
static
strictfp
synchronized
transient
volatile
控制
Control
流程控制
Flow Control
case
default
do
else
for
if
switch
while
流程中断
Flow Interruption
break
continue
return
异常处理
Exception Handling
assert
catch
finally
throw
throws
try
特殊保留
Reserved
文字值
Literal Value
null
true
false
未使用
Not Used
const
goto

Read More →

Facebook
Twitter
LinkedIn
RSS
ZhiHu

Recent Posts

  • 三年居家工作感受
  • Pixel Watch智能手表和Pixel 5, 6 Pro 及 7 Pro手机
  • 我拥有过的无线耳机
  • 毕业工作一个月,我差点被开除
  • 我拥有过的移动硬盘
  • ProtoBuf 2.0 method count optimization for android development
  • 面过100场行为面试后

Categories

  • Algorithm Analysis(算法分析)
  • Article Collection(聚宝收藏)
  • Data Structures(数据结构)
  • Design Patterns(设计模式)
  • English Posts(英文写作)
  • Front Interface(界面构想)
  • IT Products(数码产品)
  • Knowledge Base(心得笔库)
  • Language Tips(语言初试)
  • Mathematical Theory(数学理论)
  • Mobile Development(移动开发)
  • Programming Life(程序人生)
  • Reading Notes(阅而后知)
  • Software Engineering(软件工程)
  • Special Tricks(奇技妙招)
  • Tangential Speech(漫话杂谈)

Tags

Aero Android API Bash Binary Search Bitwise Operation Book C/C++ Career Chrome Conference CSS Debug Device DOM Extension Framework Game Gradle Hearthstone HTML Initialization Intellij Interview iOS Java JavaScript jQuery Keyword Language Issues Mac Microsoft Mobile Modifier Objective-C PHP Principle Reference Regular Expression Static String Tools Tutorial UI XML

Blogroll

  • Ahmed's Blog
  • Gert Lombard's Blog
  • Gordon Luk
  • Jack & Allison
  • 开发部落

Archives

Designed using Chromatic. Powered by WordPress.