访问控制修饰符
在Java中提供了四种访问权限,使用不同的访问权限修饰符修饰时,被修饰的内容会有不同的访问权限
- public:公共的
- protected:受保护的
- default:默认的
- private:私有的
修饰符 | 当前类 | 同包 | 子类 | 其他包 |
---|---|---|---|---|
public | Y | Y | Y | Y |
protected | Y | Y | Y | |
default | Y | Y | ||
private | Y |
编写代码时,如果没有特殊的考虑,建议这样使用权限:
- 成员变量使用 private ,隐藏细节
- 构造方法使用 public ,方便创建对象
- 成员方法使用 public ,方便调用方法
主函数public static void main(String[] args)
解析
- public:公共的意思,是最大权限修饰符
- static:由于 JVM 调用 main 方法的时候,没有创建对象(只能通过类名调用。main必须用static修饰)
- void:由于main方法是被 JVM 调用,不需要返回值
- main:主要的意思,所以 JVM 采用了这个名字作为程序的入口
- String[]:字符串数组
- args:数组名(在运行的时候,通过 Java 命令给 args 数组赋值)
静态-static
静态基本介绍
静态:该区域中的数据在整个程序运行期间一直占用这些存储空间,也可认为是其内存地址不变,直到整个程序运行结束
特点:
- 随着类的加载而加载
- 优先于对象存在
- 对所有对象共享
- 可以被类名直接调用
优点:
- 对对象的共享数据进行单独空间的存储,节省内存,没有必要每个对象都存储一份
- 可直接被类名调用
缺点:
- 生命周期过长,随着类的消失而消失
- 访问出现权限,即静态虽好但只能访问静态
静态存在形式
静态变量
- 调用:访问类变量;
类名.类变量名;
- 特点:
- 实例字段在每个实例中都有自己的一个独立“空间”,但静态字段只有一个共享“空间”,所有实例都会共享该字段
- 所有的全局变量都是静态变量,而局部变量只有定义时加上类型修饰符static,才为局部静态变量
- 局部静态变量占用内存时间较长,并且可读性差,因此,除非必要,尽量避免使用局部静态变量
内存分配 | 生存周期 | 调用方式 | 共享方式 | 访问方式 | |
---|---|---|---|---|---|
实例变量 | 需要被实例化后才会分配内存 | 取决于实例化的类的存在周期 | 当该变量所在的类被实例化后,可通过实例名直接访问 | 局部变量,不共享 | 可以访问静态成员 |
静态变量 | 在应用程序初始化时,存在内存当中,直到它所在的类的程序运行结束时才消亡 | 应用程序的存在周期 | 只能通过“类.静态变量名”调用,类的实例不能调用 | 全局变量,被所有类的实例对象共享,即一个实例的改变了静态变量的值,其他同类的实例读到的就是变化后的值 | 不能访问非静态成员 |
阐述静态变量和实例变量的区别?
- 静态变量: static修饰的变量(类变量),属于类不属于类的任何一个对象,类不管创建多少个对象,静态变量在内存中有且仅有一个拷贝
- 实例变量: 必须依存于某一实例,需要先创建对象然后通过对象才能访问到它。静态变量可以实现让多个对象共享内存
静态方法
-
调用:static 修饰成员方法时,该方法称为类方法。静态方法在声明中有 static,使用类名来调用,而不需要创建类的对象
类名.静态方法名(参数);
-
格式:
权限修饰符 static 返回值类型 方法名 (参数列表){ // 执行语句 }
-
特点
- 调用实例方法必须通过一个实例变量,而调用静态方法则不需要实例变量,通过类名就可以调用因为静态方法属于 Class 而不属于实例。因此,静态方法内部,无法访问this变量,也无法访问普通成员变量或成员方法,静态方法可以直接访问静态变量和静态方法
- 静态方法不能直接访问普通成员变量或成员方法;成员方法可以直接访问类变量或静态方法
- 静态方法经常用于工具类。例如:Arrays.sort()、Math.random()
静态代码块
- 位置:类中方法外
- 执行:随着类的加载而执行且执行一次,优先于main方法和构造方法的执行
- 格式:
public class ClassName{ static { // 执行语句 } }
- 格式:
- 特点:
- 随着类的加载而执行,只执行一次,并优先于 main 函数,静态代码块是由类调用的。类调用时,先执行静态代码块,然后才执行主函数态代码块其实就是给类初始化的,而构造代码块是给对象初始化的(静态代码块--构造代码块--构造方法)
- 静态代码块中的变量是局部变量,与普通函数中的局部变量性质没有区别
- 一个类中可以有多个静态代码块
不可变-final
子类可以在父类的基础上改写父类内容,比如,方法重写。那么我们能不能随意的继承 API 中提供的类,改写其内容呢?显然这是不合适的。为了避免这种随意改写的情况,Java 提供了 final 关键字,用于修饰不可改变内容
final:不可改变。可以用于修饰类、方法和变量
- 类:被修饰的类,不能被继承
final class 类名 { //方法体 }
- 方法:被修饰的方法,不能被重写,必须被赋值即常量
权限修饰符 final 返回值类型 方法名(参数列表){ //方法体 }
- 变量:
基本数据类型变量:被修饰的变量,不能被重新赋值
引用类型的变量:初始化之后便不能再让其指向另一对象,即内存地址不能再更改。但不影响对象内部的成员变量值的修改
特点:
- 当用 final 修饰⼀个类时,表明这个类不能被继承。final类中的所有成员方法都会被隐式地指定为 final
- 对于⼀个 final 变量,如果是基本数据类型的变量,则其数值⼀旦在初始化之后便不能更改
如果是引⽤类型的变量,则在对其初始化之后便不能再让其指向另⼀个对象
使用 final 的原因:
- 锁定,以防任何继承类修改它的含义
- 效率。在早期 Java 实现版本中,会将 final 方法转为内嵌调用
final 修饰基本数据类型变量不能再赋值
public class finalDemo {
public static void main(String[] args) {
//final修饰变量
final int a = 10;
//再赋值,报错(已分配assigned,不可再赋值)
//a = 20;
}
}
public class finalDemo {
public static void main(String[] args) {
// final修饰变量
final int a = 10;
for (int i = 0; i < 10; i++) {
a = i;
System.out.print(a + "、");
}
// 每循环一次,都是一个新变量
for (int i = 0; i < 10; i++) {
final int a = i;
System.out.print(a + "、");
}
}
}
final 修饰引用类型的变量:初始化之后便不能再让其指向另一对象,但不影响对象内部的成员变量值的修改
public class finalDemo {
public static void main(String[] args) {
final Student student1 = new Student("张三",21);
Student student2 = new Student("李四",12);
// student1 = student2; // 报错
student2 = student1;
student1.setAge(100);
System.out.println(student1.getAge()); //100
}
}
抽象-abstract
同步锁-synchronized
synchronized 声明的方法同一时间只能被一个线程访问。synchronized 修饰符可以应用于四个访问修饰符
格式:
public synchronized void showDetails(){
.......
}
volatile
volatile 修饰的成员变量在每次被线程访问时,都强制从共享内存中重新读取该成员变量的值。而且,当成员变量发生变化时,会强制线程将变化值回写到共享内存。这样在任何时刻,两个不同的线程总是看到某个成员变量的同一个值
public class MyRunnable implements Runnable {
private volatile boolean active;
public void run() {
active = true;
while (active) { // 第一行
// 代码
}
}
public void stop() {
active = false; // 第二行
}
}
通常情况下,在一个线程调用 run() 方法(在 Runnable 开启的线程),在另一个线程调用 stop() 方法。
如果第一行中缓冲区的 active 值被使用,那么在第二行的 active 值为 false 时循环不会停止。但是以上代码中我们使用了 volatile 修饰 active,所以该循环会停止