Skip to content
Ider

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

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

Language Tips(语言初试)

2020-03-12
12 March
On March 12, 2020
In Data Structures(数据结构), Knowledge Base(心得笔库), Language Tips(语言初试), Mobile Development(移动开发)

基础数据类型的包装类的内存大小

谈到 Java 的基础数据类型(Primitive Data Types),可以找到很多关于其种类以及内存大小的文章。基础数据类型对应的包装类(Wrapper Classes)也会介绍自动装箱(Autoboxing)和拆箱(Unboxing),以及带来的效率上的影响。但可能是因为Java一般所使用的环境都有比较充足的内存,似乎从来没有人讨论过每种包装类所占的内存大小以及对内存溢出可能造成的影响。

到了 Android 应用程序开发领域,由于设备硬件本身以及系统对各个程序在资源使用上的约束,就会出现很多特别的使用习惯,比如推荐使用Typedef来替代枚举类型(enum)来减少内存使用和方法数。因此关于基础数据类型的包装类所占内存大小就值得写篇博客来研究一下。

包装类所占内存大小

网上我找了很多计算类内存大小的方法,但是既然我是 Android 工程师,我就用 Android Studio 提供的 Memory Profiler 来查看内存使用情况

下边就是我用来做内存检查的类,它包含了所有基础数据类型和对于的包类型

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

public class Primitives {
  boolean pBoolean;
  byte pByte;
  char pChar;
  double pDouble;
  float pFloat;
  int pInt;
  long pLong;
  short pShort;

  Boolean cBoolean;
  Byte cByte;
  Character cChar;
  Double cDouble;
  Float cFloat;
  Integer cInt;
  Long cLong;
  Short cShort;

  public Primitives() {}

  public Primitives(Void v) {
    cBoolean = false;
    cByte = 0;
    cChar = '\0';
    cDouble = 0.0;
    cFloat = 0.0f;
    cInt = 0;
    cLong = 0L;
    cShort = 0;
  }
}

[/codesyntax]

接着在 MainActivity 中创建两个 Primitives 类的字段:一个没有初始化对象类型,一个将所有对象类型初始化成默认值。然后运行程序,在 Android Studio 中打开 Profiler 查看内存时”Dump Java heap” 就可以看到这两个 Primitives 对象内存使用情况

从图中的结果可以看出如果对象的值是 null,那他们是不占用空间。在这一点上看,如果字段大部分时间都存的是空值或者以空值指代默认值,那相对比总是会被赋予默认值的基础数据类型在内存使用方面要好一些。

对于包装类,它们都比其对应基础数据类型要占用更多的内存空。仔细对比不难计算出他们的差别都是 8 bytes,正好是一个普通 Object 实例的大小。这也证明的包装类只是对基础数据类型的简单包装。

基础数据类型 包装类型
类型 大小 类型 大小
boolean 1 byte Boolean 9 bytes
byte 1 byte Byte 9 bytes
char 2 bytes Character 10 bytes
double 8 bytes Double 16 bytes
float 4 bytes Float 12 bytes
int 4 bytes Integer 12 bytes
long 8 bytes Long 16 bytes
short 2 bytes Short 10 bytes

所以,使用包装类不仅会有自动装箱和拆箱产生的效率上的影响,对内存的使用也有很大的影响。而 Android 的内存又是稀缺资源,所以 Android 库里多了特殊的数据类型来优化内存的使用,比如 SparseArray 替换以 int 为主键的 HashMap。
Read More →

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 →

2017-08-28
28 August
On August 28, 2017
In Knowledge Base(心得笔库), Language Tips(语言初试), Software Engineering(软件工程)

Android单元测试有关资源

做Android开发,实在有太多的开源库和框架供开发者选用,总是让人无所适从到底哪个才是真正合适的。但是在自动化单元测试这一块,库的选择反而少了许多。可能是这些库实在太优秀了,也可能自动化测试的受重视程度相对较低所以没有太多人投入研发之中。

但是,自动化测试特别是单元测试(Unit Test)对于提高代码质量有很大的帮助。本文就来介绍一些Android中常用的单元测试库。

Junit

Junit是最主流的Java自动化则是框架,目前发展到Junit4。通过各种Java注解(Annotation)来标识测试的基本信息。
每个测试经历几个流程:设置,执行,验证。上手简单,运行快速,结果直观。API设计也很简洁,很容易扩展。

Mockito

单元测试最重要是减少测试对象的依赖,专注在该测试对象上。Mockito框架能很好的帮助我们完成这一目标,它在运行时将部分类在二进制层面替换掉,如此测试就不用依赖在实际代码上。
另外,单元测试中的断言验证(assertion)一般都是基于对象的状态(state),而Mockito提供了基于行为(behavior)的验证方式。

Robolectric

在Android的代码则很大程度依赖于Android的系统库,比如Context,View等等。其他几个库都是基于Java开发出来的,在单元测试中无法调用到Android系统的代码。Roboletric的出现就很好的解决了这个问题,它为测试提供了Android的系统代码, 并且通过配置可以指定运行的API等级。

AssertJ

JUnit自带assert方法来验证测试结果,这些方法使用了Hamcrest的函数式API。AssertJ则更具面向对象设计,并且是流式接口(fluent interface),让代码书写更加流畅,在IDE中也有更好的补全功能。
另外AssertJ扩展性也非常强,对于各种常用的Java库都有相应的扩展来更好的适应单元测试,比如Guava。即使是Android也有开源扩展库。
Read More →

2017-06-07
07 June
On June 7, 2017
In Article Collection(聚宝收藏), Language Tips(语言初试)

Intellij的PSI Viewer工具

前阵子研究了一下怎么创建自定义的Intellij的Plugin和Android的Lint Rule,惊喜地发现它们有一个共同点就是都用PSI (Program Structure Interface)来呈现代码结构。因此很容易重用部分代码来将某个需求通过Plugin和Lint Rule来实现。

可惜关于PSI的文档十分稀少,所以本来就来讨论Intellij中查看PSI结构的工具来更多得在代码实现中读取和操作PSI对象。

Intellij PSI Viewer

Intellij内置了PSI Viewer,可以从Tools菜单下找到相应的选项来打开窗口,如果当前激活的Intellij窗口是文件编辑窗口还能看到选项直接常看当前文件的PSI结构:

打开PSI Viewer窗口,可以看到如下的视图进行PSI结构查阅,当鼠标指针在文件中移动时,对应的PSI结构也会被加亮来表示它属于哪个类型。这样在代码中就可以知道应该给PsiTreeUtil.getChildrenOfType()传什么参数了,或者在强制转换时应该用什么类型。

Read More →

2017-05-03
03 May
On May 3, 2017
In Knowledge Base(心得笔库), Language Tips(语言初试), Mobile Development(移动开发)

Android编译工具Gradle和Buck使用对比

前面介绍了Gradle和BUCK两个编译工具的基本概念,本篇文章再来说说日常使用情况。本文不涉及配置方面的使用,毕竟大多时候配置工作是不重复的任务,具体操作可参见官方文档。这里主要讲讲日常工作会用到的使用两个工具编译,安装等操作的不同体验。

指令(Command)

指令操作是程序自动话的先决条件,两者都是基于指令来执行编译任务。其主指令分别为./gradlew和buck,然后调用子指令来执行相应的操作。
Read More →

2017-02-27
27 February
On February 27, 2017
In Knowledge Base(心得笔库), Language Tips(语言初试), Mobile Development(移动开发)

Android编译工具Gradle和Buck浅对比

在Twitter工作的那两年,我学习了Gradle编译Android,了解了很多Gradle的配置;在Facebook的一年里我又学习了Buck编译工具,跟Buck的开发人员有过交流。现在市场上主流的Android编译工具还是Google官方推行的Gradle,Github上很多Android开源项目也会带上Gradle文件,只有很少的项目会选用Buck。

本文就来分享一些我对两者的使用体验。不过我对编译工具的使用还停留在当做配置文件的阶段,还没有到达当做开发工具做深入自定义的程度,也没有足够的经验来总结说Gradle和Buck两个编译工具到底哪个比另一个更好,所以只能描述一些比较浅显的表层对比。

语言(Language)

Gradle使用的语言是Groovy。为了能更好的了解Gradle,我曾特尝试学习了Groovy,它算是Java的超集,可以跟Java无缝结合,只不过Groovy加入了诸多丰富的元素来弥补Java的不足。其中一点就是Groovy的闭包形式似乎又结合了JavaScript里的概念,却又不是非常成熟,当年学习的时候没有特别能领悟其中的奥秘就中途放弃了。另一方面,Gradle实际上是Groovy提供的“领域特定语言(Domain-specific language, DSL)”,然后用Groovy的语法解析去执行。然后再读Gradle的文档了解提供的特定API,内容无比繁多。好在Google总结了Android开发需要的内容,平时也只要复制粘贴必要的内容即可。

Buck使用的语言是Python。可以在BUCK文件里书写任何Python指令来执行,比如动态配置源码范围等等。但最长使用的还是对Buck规则做一层包装来进行自定义规则。不过通过跟Buck的开发人员的交流,Buck实际上是用Java开发出来的,在编译时开启了Python环境来编译Buck。

 

对比Groovy和Python,前者几乎没有听到过其他的应用,深入学习不会带来其他好处,后者应用广泛,但是在Android开发上也不会有更多帮助。再者,要使用Gradle和Buck这两个编译工具,也并不需要对相应的两门语言有深入的理解,只要掌握基本使用即可。

模块(Module)

这里的模块是指Intellij的项目模块,是为了更好的组织、测试、重用而创建的独立单元。每个模块都有它的属性:比如主模块是应用程序,此外我们会创建资源模块,Java库模块,Android库模块等等。当开发多个应用程序时,就可以引入部分模块来重用。
Read More →

2016-07-31
31 July
On July 31, 2016
In Article Collection(聚宝收藏), Knowledge Base(心得笔库), Language Tips(语言初试)

编程术语鉴别-函数篇

常说“隔行如隔山”,每个行业都只有该业内人士才懂的术语。软件行业里也有很多别人听不懂的话,比如有个笑话就说:所有讲不清楚的过程都叫做“算法(Algorithm)”。但其实即使身处在软件行业里,也任然有好多模棱两可的的概念,常常被混淆使用。就让本文来梳理一下这些概念的细微区别。

function vs. method

函数(function)和方法(method)在编程中的概念就很相近,经常被互换着称呼,但它们的区别主要是在定义的位置上。

function是一段通过名字调用的代码,它可以接受数据进行操作,并可能会有返回值。
method是一段通过关联在对象上的名字调用的代码。

从这段解释上看函数和方法基本一致,只是方法是属于对象的一部分,所以在面向对象语言中才有概念。如果是C语句,就只有函数的说法。

方法是附属于对象的,相对于函数可以接受传入参数,对象本身也会作为隐性参数可以在方法中被调用。在返回值方面,方法不仅可以选择返回数据,还可以将数据赋予其所属的对象。

(在我看来,计算机函数的概念应该与数学的“函数”概念是相通的,只是数学上的函数必须要输入和输出。如果考虑到计算机函数的参数的类型,以及异常(Exception)的情况,则感觉与数学的“映射”更接近了。)

References:

  • Difference between a method and a function – Stack Overflow
  • Subroutine – Wikipedia, the free encyclopedia
  • programming practices – Method vs Function vs Procedure – Programmers Stack Exchange

parameter vs. argument

parameter和argument都被翻译成“参数”,它们也经常被混淆。其实很好更精准的翻译应该是“形参(formal parameter)”和“实参(actual argument)”。

parameters是定义函数时定义的那些变量,它们是函数类型识别(type signature)的一部分;
arguments是调用函数时传入到函数中的那些变量,它们是函数的输入值。

这称为也是相对的,比如说下边的例子:
[codesyntax lang=”java” lines=”normal”]

void foo(int x, int y) {
    bar(x, y);
}

[/codesyntax]
x和y对于foo函数来说是形参(parameter),而对于bar来说是实参(argument)。因此也可以说argument是parameter的实例。

References:

  • Parameter (computer programming) – Wikipedia, the free encyclopedia
  • function – “Parameter” vs “Argument” – Stack Overflow
  • language agnostic – Difference between parameter and argument – Stack Overflow

declaration vs. definition

申明(declaration)和定义(definition)这两个概念应该缘起于C/C++,因为写C/C++程序会分头文件(header files)和实现文件(implementation files)。方法和变量的申明部分一般放在头文件,定义部分则在实现文件里。

所以很容易理解

declaration是描述变量、方法、类的类型标识,它没有具体的实现;编译器看到申明部分不会分配内存,而是从其它地方找到其实现的定义。
definition是真正实例化变量,实现方法和类。编译器看到定义部分时就知道如何分配内存,在运行时分配相应大小内存去存放内容。

比如下边这些代码都属于申明:

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

extern int bar;
extern int g(int, int);
double f(int, double); // extern can be omitted for function declarations
class foo; // no extern allowed for type declarations

[/codesyntax]

而下边的代码是对上述“申明”的具体“定义”:

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

int bar;
int g(int lhs, int rhs) {return lhs*rhs;}
double f(int i, double d) {return i+d;}
class foo {};

[/codesyntax]

不严格的说“定义”也包含了“申明”的部分,应该单看定义中变量,方法和类的名字部分,那些可以算是“申明”。

 

在Java中并没有“申明”的概念,所有内容都是“申明”即“定义”。如果实在要说细分“申明”,不严格的讲可以把接口(interface)和抽象方法(abstract method)看做“申明”。但实际中我们还是会说“定义接口”,“定义抽象函数”。

References:

  • c++ – What is the difference between a definition and a declaration? – Stack Overflow
  • What’s the difference between declaring and defining in C and C++ – Cprogramming.com
  • What is the difference between declaration and definition in Java? – Stack Overflow
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-31
31 October
On October 31, 2015
In Design Patterns(设计模式), Knowledge Base(心得笔库), Language Tips(语言初试)

Java修饰符及其作用对象

在《Java关键字分类解析》一文里已经对Java的所有关键字进行了分类归组,并对部分关键字做了一些简单的介绍分析。不过对于修饰符这部分值得更详细的探讨,所以本文就来讲述下这些修饰符在Java中的功能及应用。

Java的关键字里总共有11种修饰符,但实际上还有一种访问修饰符(Access Modifier),那就是“没有修饰”的修饰符,也就是不加任何修饰符在作用对象上。这种修饰符没有固定名称,以下都是出现过的的名字:“默认(default)”、“无修饰(No Modifier)”、“包私有(Package-Private)”、“包可见(Package)”。本文将以(package)来表示该隐形的修饰符,然后针对一共12种修饰符来作阐述。

对于所有Java的概念,可以应用修饰符的对象有三种:类(Class)、方法(Method)、变量(Variable)。进一步考虑,Java可以在类的定义里定义另一个类,所以对于类定义的位置又分出:顶层类(Top-level Class),即直接定义在文件包下的类;和嵌套类(Nested Class)。对于变量,根据其是定义在类中还是方法中,可分别定义为:类字段(Class Field)和局部变量(Local Variable)。

再进一步分类的话,嵌套类还可以分成静态嵌套类(Static Nested Class)和内部类(Inner Class),不过这只是static修饰符起的效果,所以不进一步区分。同样的对于方法也不区分静态方法和对象方法,对字段也不分静态字段(Static Field)和实例变量(Instance Variable)。对于局部变量,其实还可以细分出方法参数(Method Parameter),但它的效果基本跟方法内直接定义的变量效果一致,所以不做区分。这里也不对接口(interface)进行讨论,因为它基本相当于是完全抽象类(abstract class)。

这样就得到了5种基本的修饰符作用对象,但不是所有的修饰符都可以作用在每一种对象上,所以把12种修饰符在Java中实际可作用的对象总结成下表:

Modifier Class Method Variable
Top-Level Class Nested Class Class Field Local Variable
private NO YES YES YES NO
protected NO YES YES YES NO
public YES YES YES YES NO
(package) YES YES YES YES –
abstract YES YES YES NO NO
final YES YES YES YES YES
native NO NO YES NO NO
static NO YES YES YES NO
strictfp YES YES YES NO NO
synchronized NO NO YES NO NO
transient NO NO NO YES NO
volatile NO NO NO YES NO

Read More →

Posts pagination

1 2 … 4 Next
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.