Java揭秘:深入浅出局部变量与常量池

被访问被调用的范围

  • public : 全部
  • 默认 : 同包
  • protected : 同包或有继承关系的类
  • private : 只有同类

在 A 类中定义属性和方法:

  • public: 这个类的实例化对象可以访问;
  • 默认: 如果在同包下定义一个主类, 然后在主类的主方法里面 new A 的一个对象出来, 则这个对象可以访问被默认修饰的属性和方法;
  • protected: 在同一个包下的类中 new 的 A 类对象和继承了 A 类的相同或不同包下的子类中 new A 类对象, 这个对象能调用 A 类中所有的属性和方法 (不同包下没有继承关系的类不能访问)
  • private: 只有这个类中的属性和成员方法能访问

geter 和 setter 更深入理解

  • 当把类的属性和方法访问权限设置为 private 时, 一般都提供上面 2 个公共方法;
    在这 2 个方法体内, 可以添加一些限制代码;
    比如说 setter 方法, 需要用户输入账号密码才有权限修改
    getter 也可以这么做, 还可以在返回值的时候做一些动作;
    这就是隐藏.
  • 也可以用 private 修饰 setter 方法, 那么这个属性就是只读;
  • 用 private 修饰 getter 方法, 这个属性就是只写;

封装的好处

  1. 类的成员变量可以成为只读或只写
  2. 类可以对存储在其成员变量中的内容有一个整体的控制
  3. 类的用户不需要知道类是如何存储数据的

static 关键字 (类级别的, 与对象无关)

  1. static 修饰的全局变量叫静态变量, 也叫类变量, 被所有的对象所共享, 用类名. 属性访问;
  2. static 修饰的方法叫静态方法, 即类方法; 可以直接用类名. 方法访问;
  3. 类只能使用类方法, 而不能使用静态变量;
  4. 类加载以后就存在内存中了, 在 mian 方法执行之前;
  5. 用 static 修饰的变量存在堆内存的数据段的静态区中

public static final int MAX = 100;

常量属性定义为 static, 节约空间
final: 因为用 final 修饰, 所以是个常量
public: 因为是常量, 所以不能更改, public 修饰给别人看也没事
static: 共享, 这样就不用 new 出来才能使用;

  • 静态方法只能访问或调用静态属性和静态方法
  • 非静态方法法既能访问调用非静态属性和方法, 也能访问静态方法和属性
    原因: 静态属性和方法在类加载的时候就加载到内存中了, 而非静态属性和方法必须绑定在对象上, 而对象必须先实例化才能调用;
    如果用静态方法访问非静态属性, 但是内存中却不存在非静态属性, 所以编译器会报错.

代码块

静态初始化块

  • 语法: static {语句 1; 语句 2;…;};
  • 调用顺序: 当类被 JVM 的类加载器加载的时候执行,
    只加载一次, 因为 JVM 只加载一次类.

实例化初始化块

  • 语法:{语句 1; 语句 2;…;};
  • 调用顺序: 在类的对象每次实例化的时候执行一次, 在构造器之前调用;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
class A {
public String name = "成员属性" ;
static {
System.out.println("静态初始化块");
}

{
this.name = "name";
this.age = 26;
System.out.println("实例初始化块内");
System.out.println(this.name);
System.out.println(this.age);
}
public int age = 10;

public A ( ) {
System.out.println("构造方法内");
System.out.println(name);
System.out.println(age);
}
}
//主方法
class ClassName {
public static void main(String[] args) {
new A();
}
}

输出结果 :
静态初始化块
实例初始化块
name
构造方法内
name

步骤:

  1. 执行到 new A(); 时, 加载 A.class 到内存中;
  2. static 随着类的加载而创建 (在堆内存的数据段中的静态区中, 存放的是静态变量和静态方法, 常量存放在堆内存的数据段的常量池中)
  3. 输出 静态初始化代码块
  4. 执行构造方法时要做的事:
  • 在堆内存中申请内存;(size = int + String)
  • 分配这块内存;(给 name 分配 4 字节空间, 因为它是一个引用; 给 age 分配 4 个字节);
  • 初始化这块内存中的变量,;age=10;
  • 调用实例初始化代码块, 执行代码块中的语句 name = name,age=26;
  • 执行实例代码块后面的语句;public int age = 10;
  • 执行构造方法内的语句 输出 nameage 的值;

内部类 (先大概了解)

  • 在类的里面再定义一个类, 这个类就叫内部类;
  • 它是一个独立的类;
  • 可以产生对象使用;
  • 编译后又独立的 class 文件;
    • classA$1classB –> 是局部内部类;(比成员内部类少了一个数字, 因为一个类有多个方法, 在不同的方法里面可以定义相同名字的内部类, 为了区分, 编译器自动为我们添加你一个数字作为区分)
    • classA$classB –> 是成员内部类;(把成员内部类作为外部类的属性, 属性当然不能重名了, 所以成员内部类的名字不能相同)

分类

  • 成员内部类 –> 定义在外部类中方法外面的类;
    • 静态内部类:(成员内部类的一种)
  • 局部内部类 –> 定义在外部类的方法里面, 定义的时候不能加访问修饰符
    • 匿名内部类: 没名字, 主要用于实现接口的方法.

为什么局部内部类不能用访问修饰符修饰?

从内存中看,当方法里的局部变量所在方法结束时,该变量即在栈内存中消失;而内部类其实是一个类,只有内存中对它的所有引用都消失后,该内部类才”死亡”,即内部类的生命周期可能比局部变量长。如果局部内部类能访问一般的局部变量,则在多线程中,可能当方法结束后,局部内部类 (是线程类时) 还在使用局部变量。为了避免方法内的变量脱离方法而存在的现象发生,于是 java 规定局部内部类不能访问一般的局部变量。但能访问被 final 修饰的局部变量。因为常量放在堆内存中的数据段的常量池中