修饰符
介绍
什么是修饰符
一般用来定义类、方法、变量,通常在语句的最前端。
修饰符决定了一个类、方法、变量的可使用程度。
分类
访问修饰符
非访问修饰符
访问权限修饰符
可以用来保护对类、变量、构造方法、普通方法的访问。
- public(公有访问修饰符)
可以修饰类、变量、构造方法、接口、普通方法。
- 同一包内的类之间共享。
- 不同包之间导包后也可共享。
- 继承关系能共享。
//public修饰类
public class TestPublic{
public String name;//public修饰类变量
public TestPublic(){}//public修饰构造方法
public void test1(){}//public修饰普通方法
}
//public修饰接口
public interface TestInterface{
//抽象类是有抽象方法,也有非抽象方法。
//接口中的方法都是默认抽象的,接口其实可以理解成一种特殊的抽象类。
//接口中的成员变量默认都是public static final修饰的。所有的属性都是静态的常量。
//接口中的所有方法默认都是public abstract修饰的。
//接口没有构造方法(构造方法用于创建对象)。
(public static final) String name = "小明";//默认public static final修饰变量
(public abstract) void test();//默认public abstract修饰方法
}
- protected(受保护的访问修饰符)(仅子类可见性)
可以修饰类中的变量、方法、构造方法。
不能修饰接口中的变量、方法。
不能修饰类和接口。
同包的不同类之间共享(这里的不同类指的是子类)。
不同包中的子类共享。
public class TestProtected{
protected int age;//protected修饰成员变量(子类可见)
protected void getAge(){}//protected修饰方法(子类可见)
protected void TestProtected(){}//protected修饰构造方法
}
public interface TestInterface{
//protected不能修饰接口、接口的变量、接口中的方法。
(public static final) int age = 10;//默认public static final修饰变量
(public abstract) void getAge();//默认public abstract修饰方法
}
- default(默认访问修饰符)
不使用任何关键字。
可以修饰变量、方法。
同包内的类间相互可见。
接口隐式声明的变量是public static final修饰的。
接口里的方法默认情况是public修饰的。
public class TestDefault{
int age;//修饰变量
void getAge(){}//修饰方法
TestDefault(){}//修饰构造方法
}
public interface TestInterface{
(public static final) int age; //默认public static final修饰变量
(public abstract) void getAge();//默认public abstract修饰方法
}
- private(私有访问修饰符)
最严格的访问级别。
可以修饰类的变量、方法、构造方法。
不能修饰接口的变量和方法。
不能用来声明类和接口。
只能被当前类访问。
外部类可以通过公共的getter方法访问声明的private属性。
主要功能:
- 隐藏类的实现细节和保护类的数据。
public class TestPrivate{
private int age;//private修饰变量
private TestPrivate(){}//private修饰构造方法
private void getAge(){}//private修饰普通方法
}
public interface TestInterface{
(public static final) int age; //默认public static final修饰变量
(public abstract) void getAge();//默认public abstract修饰方法
}
非访问修饰符
- static:静态的
修饰类变量、类方法。
声明独立于对象的静态变量:
- 静态变量只有一份拷贝。
- 也叫做类变量。
- 可以直接使用变量名,也可以用类名+点+静态变量名的方式调用。
独立于对象的静态方法:
- 不能使用类的非静态变量。
- 可以直接调用静态方法名,也可以直接用类名+点+静态方法名的方式调用。
//这是一个普通类
public class TestModifierStatic {
public String name;//非静态变量
public static int age = 10;//静态变量(类变量)
//静态方法,可以在未实例化对象前直接调用方法名;也可以采用TestModifierStatic.getAge()的方式调用
public static int getAge(){
//静态方法中不能调用非静态的变量,否则编译报错:
//Cannot make a static reference to the non-static field TestModifierStatic.name
// System.out.println(TestModifierStatic.name);//编译报错
System.out.println("静态变量是:"+TestModifierStatic.age);//静态变量是:10
System.out.println("这里是静态方法");//这里是静态方法
return age;//可以直接使用静态变量
}
//非静态方法,只能在实例化对象后通过对象+点+方法名的方式调用
public void getName(String name){
//非静态方法中可以使用非静态变量,也可以使用静态变量
System.out.println(name);//王二 非静态变量
System.out.println(age);//10 静态变量
System.out.println("这里是非静态方法");//这里是非静态方法
}
public static void main(String[] args) {
//静态方法可以在未实例化对象前直接调用
getAge();
// getName();//编译报错:Cannot make a static reference to the non-static method getName() from the type TestModifierStatic
//非静态方法之后在实例化之后才能调用
TestModifierStatic tms = new TestModifierStatic();
tms.getName("王二");
}
}
- abstract:抽象的
声明抽象类、抽象方法。
- final:最终的
修饰类、方法、变量。
修饰的类不能被继承。
修饰的方法不能被重写。
修饰的变量为常量,不能被修改。
变量能被显示的初始化,并且只初始化一次。
声明的final对象的引用不能指向不同的对象,对象中的数据可以改变。
final通常和static共用声明类常量。
//final修饰的类,表示最终的类,不能被继承
public final class TestModifierFinal {}
//编译错误,final修饰的类是最终类,不能被继承
//The type TestMain cannot subclass the final class TestModifierFinal
public class TestMain extends TestModifierFinal{}
//final修饰的变量为常量,要初始化
//public final String name;//final修饰的变量需要初始化,否则编译错误:The blank final field name may not have been initialized
public final String name = "李二";//final修饰变量为常量,要初始化
//final修饰的方法不能被重写
public class TestModifierFinal {
public final String name = "李二";//final修饰变量为常量,要初始化
public final static int age = 10;//final 组合static声明类常量
//final修饰的方法,表示最终方法,不能被重写
public final void getName(){
System.out.println(name);//李二
System.out.println(age);//10
System.out.println("final修饰的方法");//final修饰的方法
}
//普通方法
public void getAge(){
System.out.println(name);//李二
System.out.println(age);//10
System.out.println("普通方法");//普通方法
}
public static void main(String[] args) {
TestModifierFinal tmf = new TestModifierFinal();
tmf.getName();
tmf.getAge();
/*运行结果为:
李二
10
final修饰的方法
李二
10
普通方法*/
}
}
public class TestMain extends TestModifierFinal{
//final修饰的父类方法不能被重写
//编译错误:Cannot override the final method from TestModifierFinal
// public void getName(){}
public void getAge(){
System.out.println("子类重写父类方法");//子类重写父类方法
}
public static void main(String[] args) {
TestModifierFinal tmf = new TestMain();
tmf.getAge();
tmf.getName();//子类不能重写父类的final方法,但是可以调用
/*运行结果为:
子类重写父类方法
李二
10
final修饰的方法*/
}
}
- synchronized:
是一种同步锁。
声明的方法同一时间只能被一个线程访问。
- 修饰代码块:被修饰的代码块被称作同步语句块;作用范围时{}内;作用对象是调用同步语句块的对象。
- 修饰方法:被修饰的方法称为同步方法;作用范围是整个方法;作用对象是调用这个方法的对象。
- 修饰静态方法:被修饰的静态方法叫做静态同步方法;作用范围是整个静态方法;作用对象是整个类的所有对象。
- 修饰类:作用范围是整个类;作用对象是整个类中的所有对象。
- synchronized修饰代码块
public class TestModifierSynchronized implements Runnable{
private static int count;//声明一个全局变量 计数器
//声明构造方法
public TestModifierSynchronized() {
count = 0;
}
//重写run()方法
@Override
public void run() {
// TODO Auto-generated method stub
synchronized (this) {
for (int i = 0; i < 5; i++) {
try {
System.out.println("线程名:"+Thread.currentThread().getName()+":"+(count++));
Thread.sleep(100);//相邻两个线程休眠100毫秒
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
public int getCount(){
return count;
}
}
public static void main(String[] args) {
//方式一:同时访问一个对象
TestModifierSynchronized tms1 = new TestModifierSynchronized();
Thread t1 = new Thread(tms1,"sync_thread-1");
Thread t2 = new Thread(tms1,"sync_thread-2");
t1.start();
t2.start();
/*运行结果为:
线程名:sync_thread-1:0
线程名:sync_thread-1:1
线程名:sync_thread-1:2
线程名:sync_thread-1:3
线程名:sync_thread-1:4
线程名:sync_thread-2:5
线程名:sync_thread-2:6
线程名:sync_thread-2:7
线程名:sync_thread-2:8
线程名:sync_thread-2:9*/
/*总结:两个并发线程访问同一个对象中的synchronized代码,同一时刻只有一个线程执行;
另一个线程阻塞,等当前线程执行完毕整个代码块,另一个线程才能执行该代码块。这两个线程
是互斥的,执行synchronized代码块时锁定了当前对象,执行完代码块才能释放对象所,下
一个线程才能获取对象所并执行。*/
//方式二:同时访问多个对象
TestModifierSynchronized tms2 = new TestModifierSynchronized();
TestModifierSynchronized tms3 = new TestModifierSynchronized();
Thread t3 = new Thread(tms2,"sync_thread-3");
Thread t4 = new Thread(tms3,"sync_thread-4");
t3.start();
t4.start();
/*运行结果为:
线程名:sync_thread-3:1
线程名:sync_thread-4:0
线程名:sync_thread-3:3
线程名:sync_thread-4:2
线程名:sync_thread-4:4
线程名:sync_thread-3:5
线程名:sync_thread-3:7
线程名:sync_thread-4:6
线程名:sync_thread-3:8
线程名:sync_thread-4:9*/
/*总结:因为是两个线程分别创意一个对象去执行的,所以说每个线程都是锁定了自己创建的对象,
他们相互之间没有联系。*/
}
- synchronized修饰普通方法
public class TestModifierSynchronized2 implements Runnable{
private static int count = 0;//全局变量 计数器
//synchronized修饰普通方法
public synchronized void testRun(){
for (int i = 0; i < 5; i++) {
try {
System.out.println("线程名 :" + Thread.currentThread().getName() + " 当前计数器 :" + (count++));
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//重写run方法
@Override
public void run() {
testRun();
}
}
public static void main(String[] args){
TestModifierSynchronized2 tms21 = new TestModifierSynchronized2();
Thread t5 = new Thread(tms21,"sync_thraf-5");
Thread t6 = new Thread(tms21,"sync_thraf-6");
t5.start();
t6.start();
/*运行结果为:
线程名 :sync_thraf-5 当前计数器 :0
线程名 :sync_thraf-5 当前计数器 :1
线程名 :sync_thraf-5 当前计数器 :2
线程名 :sync_thraf-5 当前计数器 :3
线程名 :sync_thraf-5 当前计数器 :4
线程名 :sync_thraf-6 当前计数器 :5
线程名 :sync_thraf-6 当前计数器 :6
线程名 :sync_thraf-6 当前计数器 :7
线程名 :sync_thraf-6 当前计数器 :8
线程名 :sync_thraf-6 当前计数器 :9*/
/*总结:synchronized修饰普通方法时的效果和修饰代码块的效果是一样的。*/
TestModifierSynchronized2 tms22 = new TestModifierSynchronized2();
TestModifierSynchronized2 tms23 = new TestModifierSynchronized2();
Thread t7 = new Thread(tms22,"sync_thrad-7");
Thread t8 = new Thread(tms23,"sync_thrad-8");
t7.start();
t8.start();
/*运行结果为:
线程名 :sync_thrad-7 当前计数器 :1
线程名 :sync_thrad-8 当前计数器 :0
线程名 :sync_thrad-7 当前计数器 :2
线程名 :sync_thrad-8 当前计数器 :3
线程名 :sync_thrad-8 当前计数器 :4
线程名 :sync_thrad-7 当前计数器 :5
线程名 :sync_thrad-8 当前计数器 :6
线程名 :sync_thrad-7 当前计数器 :7
线程名 :sync_thrad-7 当前计数器 :8
线程名 :sync_thrad-8 当前计数器 :9*/
/*总结:synchronized修饰普通方法时的效果和修饰代码块的效果是一样的。*/
}
- synchronized修饰静态方法
public class TestModifierSynchronized3 implements Runnable{
private static int count = 0;//全局变量 计数器
//重写run方法
@Override
public void run() {
testRun();
}
//synchronized修饰静态方法
public synchronized static void testRun(){
for (int i = 0; i < 5; i++) {
try {
System.out.println("线程名:"+Thread.currentThread().getName()+" 当前计数器:"+(count++));
Thread.sleep(1000);//睡眠时间
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args){
TestModifierSynchronized3 tms31 = new TestModifierSynchronized3();
Thread t9 = new Thread(tms31,"sync_thread-9");
Thread t10 = new Thread(tms31,"sync_thread-10");
t9.start();
t10.start();
/*运行结果为:
线程名:sync_thread-9 当前计数器:0
线程名:sync_thread-9 当前计数器:1
线程名:sync_thread-9 当前计数器:2
线程名:sync_thread-9 当前计数器:3
线程名:sync_thread-9 当前计数器:4
线程名:sync_thread-10 当前计数器:5
线程名:sync_thread-10 当前计数器:6
线程名:sync_thread-10 当前计数器:7
线程名:sync_thread-10 当前计数器:8
线程名:sync_thread-10 当前计数器:9*/
/*总结:因为调用了静态方法,静态方法属于类,只在加载类的时候执行一次,所以多个线程并发执行的时候
会竞争同一个对象的锁。*/
TestModifierSynchronized3 tms32 = new TestModifierSynchronized3();
TestModifierSynchronized3 tms33 = new TestModifierSynchronized3();
Thread t11 = new Thread(tms32,"sync_thread-11");
Thread t12 = new Thread(tms33,"sync_thread-12");
t11.start();
t12.start();
/*运行结果为:
线程名:sync_thread-11 当前计数器:0
线程名:sync_thread-11 当前计数器:1
线程名:sync_thread-11 当前计数器:2
线程名:sync_thread-11 当前计数器:3
线程名:sync_thread-11 当前计数器:4
线程名:sync_thread-12 当前计数器:5
线程名:sync_thread-12 当前计数器:6
线程名:sync_thread-12 当前计数器:7
线程名:sync_thread-12 当前计数器:8
线程名:sync_thread-12 当前计数器:9*/
/*总结:因为调用了静态方法,静态方法属于类,只在加载类的时候执行一次,所以多个线程并发执行的时候
会竞争同一个对象的锁。*/
}
- synchronized修饰类
public class TestModifierSynchronized4 implements Runnable{
private static int count;
//构造方法
public TestModifierSynchronized4() {
count = 0;
}
//重写方法
@Override
public void run() {
testRun();
}
//synchronized修饰类
public void testRun(){
synchronized (TestModifierSynchronized4.class) {
for (int i = 0; i < 5; i++) {
try {
System.out.println("线程名:"+Thread.currentThread().getName()+" 计数器:"+(count++));
Thread.sleep(1000);//设置休眠时间
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
public static void main(String[] args){
TestModifierSynchronized4 tms41 = new TestModifierSynchronized4();
Thread t13 = new Thread(tms41,"sync_thrad-13");
Thread t14 = new Thread(tms41,"sync_thrad-14");
t13.start();
t14.start();
/*运行结果为:
线程名:sync_thrad-13 计数器:0
线程名:sync_thrad-13 计数器:1
线程名:sync_thrad-13 计数器:2
线程名:sync_thrad-13 计数器:3
线程名:sync_thrad-13 计数器:4
线程名:sync_thrad-14 计数器:5
线程名:sync_thrad-14 计数器:6
线程名:sync_thrad-14 计数器:7
线程名:sync_thrad-14 计数器:8
线程名:sync_thrad-14 计数器:9*/
/*总结:synchronized修饰类,给整个类加锁,所有的对象用同一把锁。*/
TestModifierSynchronized4 tms42 = new TestModifierSynchronized4();
TestModifierSynchronized4 tms43 = new TestModifierSynchronized4();
Thread t15 = new Thread(tms42,"sync_thrad-15");
Thread t16 = new Thread(tms43,"sync_thrad-16");
t15.start();
t16.start();
/*运行结果为:
线程名:sync_thrad-15 计数器:0
线程名:sync_thrad-15 计数器:1
线程名:sync_thrad-15 计数器:2
线程名:sync_thrad-15 计数器:3
线程名:sync_thrad-15 计数器:4
线程名:sync_thrad-16 计数器:5
线程名:sync_thrad-16 计数器:6
线程名:sync_thrad-16 计数器:7
线程名:sync_thrad-16 计数器:8
线程名:sync_thrad-16 计数器:9*/
/*总结:synchronized修饰类,给整个类加锁,所有的对象用同一把锁。*/
}
- volatile:
只能修饰变量。
修饰的成员变量每次被线程访问时,都会从内存中重读该成员变量的最新值。
当成员变量发生变化时,强迫线程将变化值回写到共享内存中。
private volatile static int num = 0;
参考地址:
https://blog.csdn.net/u012723673/article/details/80613557
https://www.jb51.net/article/100962.htm
https://blog.csdn.net/qq_38011415/article/details/89047812