文章目录
- 权限修饰符
- private
- 缺省
- protected
- public
- 特征修饰符
- static
- final
- abstract
- volatile
- transient
- native
权限修饰符
java关于访问权限的修饰符有4个,分别为private、缺省、protected 和 public
修饰符 | 访问权限 |
private | 私有的,只能在本类中访问 |
缺省 | 缺省,不加任何修饰符时,只能同一个包内访问 |
protected | 受保护的,同一个包内或子类允许访问 |
public | 公共的,项目内都可以访问 |
private
- 私有的,可以修饰属性、方法、构造方法,内部类,需要注意的是private不能修
饰类本身,因为这会导致其它类访问不到该类,那么该类的存在就没有意义了。 - 修饰属性:通常一个类会将属性私有化,而对外提供公开的方法来访问私有的属性
- 修饰方法:被修饰的方法应该是类专属的,不需要其他类访问的方法。
- 修饰构造方法:防止其他类实例化该类,需要注意的是如果一个类只有私有化构造方法,那么该类也无法被继承,常见的用法有单例模式、工具类等
- 修饰内部类:被private修饰的内部类只有外部类和内部类能实例化。
package test;
public class PrivateTest {
// 私有化属性,而对外提供公共的方法
private int field = 0;
public int getFiled() {
return field;
}
// 私有方法
private void privateMethod() {
System.out.println("我是私有方法,只有本类的方法才能调用我");
}
//私有化构造方法,只有本类能实例化,以下为实现单例模式方法之一
private Test() {}
private static Test newInstance = new Test();
public static Test getNewInstance() {
return newInstance;
}
// 私有化内部类,只有本类和内部类可以实例化内部类
private InnerClass innerClass = new InnerClass();
private class InnerClass{
InnerClass innerClass = new InnerClass();
}
}
缺省
如果一个类、类的属性或者方法只想本包中的其他类访问,那么就可以缺省权限修饰符
package test;
/**
* 只有本包的类能访问我,继承我
*/
class DefaultTest {
int defaultfield = 0;
void defaultMethod() {
System.out.println("你好,我是test包下的类");
}
//同包才能实例化我
DefaultTest() {}
// 同包可以实例化内部类
class InnerClass{
InnerClass(){
System.out.println("hello");
}
}
}
package test;
/**
* 我也是test包下的类哟
*/
public class MainTest {
public static void main(String[] args) {
// 访问同包的类
DefaultTest defaultTest = new DefaultTest();
// 访问同包的属性
int field = defaultTest.defaultfield;
// 访问同包的方法
defaultTest.defaultMethod();
// 访问同包的内部类
DefaultTest.InnerClass innerClass = defaultTest.new InnerClass();
}
// 继承同包下的类
class SubDefaultClass extends DefaultTest{
}
}
protected
如果一个类中的属性、方法等除了本包中能访问外,还想外包的子类访问,那么就可以用protected修饰,需要注意的是protected不能修饰类本身,因为protected修饰符是想外包中的子类能够访问,如果protected修饰类的话,那么外包就无法访问到该类,也就没办法继承该类了。
public
public修饰的属性、方法、类可以在本项目中任何地方访问,但是,public修饰的属性通常是不安全的,因为任何类都可以修改属性的值。通常的,我们也应该尽可能的使访问权限最小化,即如果一个属性或方法等可以用private修饰,那么就不要用缺省甚至更大权限来修饰它,保证其最小可见性。
特征修饰符
static
- static可以修饰类的属性、方法、匿名方法(代码块)和内部类,不能修饰构造方法和类本身,被static修饰属性、方法等不需要在对象初始化之后才能使用,调用它们的方式是使用 类名.属性/类名.方法等来使用它们。
- 静态属性:被static修饰的属性是静态属性,用例:定义一个静态属性来统计网址访问次数
- 静态方法:被static修饰的普通方法是静态方法,用例:工具类的方法,用静态方法代替构造方法来实例化对象
- 静态代码块:被static修饰的匿名方法是静态代码块
- 静态内部类:被static修饰的内部类是静态内部类
final
- final修饰符可以修饰属性、方法、类,不能修饰代码块、构造方法。
- final修饰属性:被final修饰的属性,它的的引用不能改变,即该属性只能被赋值一次,不能重新赋值,但是,如果该属性指向的是一个对象/数组,那么对象/数组本身的属性/值是可以改变的。另外,如果一个属性同时被static和final修饰,那么可以将该属性当做常量看待。
package test;
public class FinalTest {
/**
* final修饰的属性
*/
private final int[] a = new int[10];
/**
* 可以修改数组的值
*/
private void modify() {
a[0] = 1;
}
/**
* 不能被重新赋值
*/
private void assign() {
// 赋值语句会报错,这里只是演示
a = new int[10];
}
}
- final修饰方法:被final修饰的方法不能被子类重写,如果设计的方法不想被重写,那么该方法应该用final修饰。
- final修饰类:被final修饰的类不能被继承,该类是不可变的,比如:String、Integer等等
abstract
- abstract 可以修饰类和普通方法,不能修饰构造方法,不能和static、final同时修饰方法
- abstract修饰类:被abstract修饰的类叫做抽象类,抽象类有构造方法用于被子类继承时使用,但是抽象类本身不能被实例化;抽象类可以有但不必须有抽象方法,但是有抽象方法的类必须是抽象类。
- abstract修饰方法:被abstract修饰的方法叫做抽象方法,抽象方法只有方法访问权限、返回值、方法名和所需参数,没有具体的实现,方法具体的实现由子类重写。之所以不能和static、final同时修饰方法是因为static修饰的方法在类加载的时候被加载,不需要对象的实例化,而final修饰的方法不能被重写。
package test;
/**
* 被abstract修饰的类是抽象类
*/
public abstract class AbstractTest {
/**
* 抽象方法,没有方法的具体实现,不能同时被static或final修饰
*/
public abstract void abstractMethod();
public static void main(String[] args) {
// 不能实例化抽象类,编译会报错,此处只是演示
new AbstractTest();
}
}
volatile
volatile用于修饰属性,它想表达的意思是该属性需要从原始地址取值,而不是寄存器中获取,关于计算机方面的知识这里不做过多介绍,这里举个例子来说明一下:
假设在一个箱子里有10个苹果,有A、B两个人来查看箱子中苹果的数量,如果没有volatile修饰,那么A、B两个人在查看完数量后就记住了有10个苹果,当其中一个人偷吃了一个苹果,那么另一个人依然以为有10个苹果,并告诉别还有10个苹果,如果有volatile修饰,那么,每当有人问箱子里还有几个苹果时,A、B都必须跑到箱子处重新数一下苹果的数量,然后告诉别人剩余的数量。
package test;
import java.util.concurrent.TimeUnit;
public class VolatileTest {
/**
* 箱子中苹果的数量
* 当没有volatile修饰时,程序进入了死循环,因为线程A无法知道有没有人偷吃了苹果
* 当有volatile修饰时,线程A在1秒后打印了有人吃了苹果
*/
// private static int appleNum = 10;
private static abstract int appleNum = 10;
public static void main(String[] args) {
// 线程A查看苹果数量
new Thread(() -> {
// 如果数量发生变化,证明有人吃了苹果
while (appleNum==10) {}
System.out.println("有人吃了苹果");
},"A").start();
// 线程B偷吃了一个苹果
new Thread(() -> {
// 这里睡1秒是为了保障线程A先去查看了苹果的数量,然后线程B再去偷吃
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {}
// 偷吃了一个苹果,数量-1
appleNum -= 1;
},"B").start();
}
}
transient
transient用作修饰属性,被修饰的属性在对象的序列化时忽略序列化,可以用在一些私密的属性上,使其忽略序列化,比如用户的密码等。
package test;
import java.io.*;
/**
* 实现Serializable 才能序列化对象
*/
public class TransientTest implements Serializable {
/**
* 用户名,可以序列化
*/
private String username = "张三";
/**
* 密码,不能序列化
*/
private transient String password = "123456";
public static void main(String[] args) throws IOException, ClassNotFoundException {
// 序列化
TransientTest transientTest = new TransientTest();
byte[] bytes;
try(ByteArrayOutputStream os = new ByteArrayOutputStream()) {
ObjectOutputStream oos = new ObjectOutputStream(os);
oos.writeObject(transientTest);
bytes = os.toByteArray();
}
// 反序列化
try(ByteArrayInputStream is = new ByteArrayInputStream(bytes)) {
ObjectInputStream ois = new ObjectInputStream(is);
Object object = ois.readObject();
System.out.println(object);
}
}
@Override
public String toString() {
return "TransientTest{" +
"username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
}
运行结果:没有transient 修饰的username属性可以正常序列号和反序列化,而password属性打印为null
native
native用作修饰方法,该方法为本地方法,不是java语言实现的,很多java基础包中的类用到此修饰符,项目开发基本用不到,作为了解即可。
以上为个人对java知识的理解和认知,希望和大家共同学习,共同进步,有什么理解错误的地方希望大家指出并多多包涵,希望和大家一起交流、探讨和学习。