八.javase-java基础语法-修饰符的使用

一.权限修饰符
在这里插入图片描述
为什么会设计这四种呢.
public修饰符,我们设计对外的API接口时必须的让别人访问到,所以得设计一个无限制的.
protected修饰符,我们设计的API接口给别人使用时,又不想随便谁都能使用,我们则用protected,让他必须继承或者实现我们的接口才能实现,也可强制的提醒他本父类还有其他工具方法可以使用.
default 修饰符,仅仅是为了我们在编写代码是,本包方便调用而已.
private 修饰符,则充分的体现了java面向对象的封装特性,很多我们不想让别人看见我们类内部的结构,我们就用private,让外部不能调用或者修改类的结构.

//public 修饰符
public class Person {
	//private修饰符
	private String name;
	//protected修饰符
	protected void say() {
		System.out.println("hello");
	}
	//default修饰符
	void jump() {
		System.out.println("jump");
	}
}

二.非访问修饰符
为了实现一些其他的功能,Java 也提供了许多非访问修饰符。

static 修饰符,用来修饰类方法和类变量。
1.修饰属性,表示此属性为静态属性,类属性.
2.修饰方法,表示此方法为静态方法,为类的方法.
3.修饰内部类,表示此内部类为静态内部类,为外部类的附庸类.
4.修饰代码块,常用来做类加载的时候加载资源使用.

//static修饰属性
	private static Animal name;
	//static 修饰方法
	protected static void say() {
		System.out.println("hello");
	}
	//static 修饰内部类,不能修饰外部类.
	public static class Chinese{
		//static 修饰代码块,常被用来加载类的资源时使用
		static {
			System.out.println("内部类的static");
		}
	}

final 修饰符,用来修饰类、方法和变量,final 修饰的类不能够被继承,修饰的方法不能被继承类重新定义,修饰的变量为常量,是不可修改的。

//final修饰类,表示最终类,不能被继承.常用来固定类的结构,如String类为final修饰的类
public final class Person1 {
	//final修饰变量,表示常量,因为值不能变,所以声明时需要赋值;如果不赋值,则需要在构造方法中创建对象的额时候给该对象赋值,且该成员变量不能是static类型的.
	public final String name ="zhangsan";
	//final修饰方法,表示此方法不能被重写,但是可以被继承和重载.
	public final void say() {
		System.out.println("hello");
	}
}

abstract 修饰符,用来创建抽象类和抽象方法以及接口。
并且注意:
1.abstract 修饰的方法不能有方法体.
2.实体类继承抽象类或者实现接口,必须实现其中全部的抽象方法.

//abstract 修饰接口,表示为抽象类
public abstract interface Person2 {
	//abstract 修饰方法,表示方法为抽象方法,没有方法体.
	public abstract void say();
}
//abstract 修饰类
abstract class Sing {
}

transient修饰符,主要用于线程的编程。即本属性不会被序列化到文件中或者是网络流中.

//transient表示禁止此属性序列化
	private transient String name;
	

synchronized的使用及原理.就是加锁的意思.即本JVM内针对该处代码必须顺序执行,不能多线程并发执行.
使用:
1.修饰方法:同步方法
2.修饰局部代码块:同步代码块

public class SynchronizedDemo {
    //同步方法
   public synchronized void doSth(){
       System.out.println("Hello World");
   }
   public void doSth1(){
	 //同步代码块
       synchronized (SynchronizedDemo.class){
           System.out.println("Hello World");
       }
   }
}

我们用javap反编译.class文件,得出如下.

public synchronized void doSth();
descriptor: ()V
flags: ACC_PUBLIC, ACC_SYNCHRONIZED
Code:
stack=2, locals=1, args_size=1
0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #3 // String Hello World
5: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: return

public void doSth1();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=2, locals=3, args_size=1
0: ldc #5 // class com/hollis/SynchronizedTest
2: dup
3: astore_1
4: monitorenter
5: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
8: ldc #3 // String Hello World
10: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
13: aload_1
14: monitorexit
15: goto 23
18: astore_2
19: aload_1
20: monitorexit
21: aload_2
22: athrow
23: return

synchronized的原理是:
1.修饰方法的时候,会在方法的开始加上标识:ACC_SYNCHRONIZED
2.修饰代码块时,会在代码起始monitorenter和结束的地方monitorexit.

无论是ACC_SYNCHRONIZED还是monitorenter、monitorexit都是基于Monitor实现的,在Java虚拟机(HotSpot)中,Monitor是基于C++实现的,由ObjectMonitor实现。

ObjectMonitor类中提供了几个方法,如enter、exit、wait、notify、notifyAll等。sychronized加锁的时候,会调用objectMonitor的enter方法,解锁的时候会调用exit方法。(关于Monitor详见深入理解多线程(四)—— Moniter的实现原理)

volatile

两个作用:
1.保证变量内存可见性.
即多线程共享变量时,每个线程会将变量拷贝一个副本进自己的线程栈中;如果使用了volatile则变量每次发生变化,都会通知使用改变量的线程,该变量发生改变.
2.禁止指令重排序.
即JVM会对指令有优化,为了更好的执行计算,如果使用了volatile关键字,则禁止了cpu的指令优化,即禁止了指令重排序.

测试可见性的作用.

public class VolatileTest extends Thread{
     //volatile boolean flag = false; //加volatile关键字,主线程修改了flag的值,则子线程会重新去取值.
     boolean flag = false;  //如果不加volatile关键字,则子线程中的值一直会是false,及会一直循环下去
     int i = 0;
     
     public void run() {
         while (!flag) {
             i++;
         }
     }
     
     public static void main(String[] args) throws Exception {
         VolatileTest vt = new VolatileTest();
         vt.start();
         Thread.sleep(2000); //主线程睡眠2秒
         vt.flag = true; //睡眠之后在修改vt的flag变量值,检查子线程是否收到flag的变化,而停止循环.
         System.out.println("stope" + vt.i);
     }
}

测试指令重排序

public class Singleton3 {
	//变量并未用volatile修饰
    private static Singleton3 instance = null;
    //使用volatile修饰变量
    //private static volatile Singleton3 instance = null;

    private Singleton3() {}

    public static Singleton3 getInstance() {
        if (instance == null) {
            synchronized(Singleton3.class) {
                if (instance == null) //这是判断
                    instance = new Singleton3();// 非原子操作,即有可能发生指令重排序,故需要将instance用volatile关键字修饰
            }
        }
        return instance;
    }
}

注意:

(1)volatile是轻量级同步机制。在访问volatile变量时不会执行加锁操作,因此也就不会使执行线程阻塞,是一种比synchronized关键字更轻量级的同步机制。
(2)volatile无法同时保证内存可见性和原子性。加锁机制既可以确保可见性又可以确保原子性,而volatile变量只能确保可见性。
(3)volatile不能修饰写入操作依赖当前值的变量。声明为volatile的简单变量如果当前值与该变量以前的值相关,那么volatile关键字不起作用,也就是说如下的表达式都不是原子操作:“count++”、“count = count+1”。
(4)当要访问的变量已在synchronized代码块中,或者为常量时,没必要使用volatile;
(5)volatile屏蔽掉了JVM中必要的代码优化,所以在效率上比较低,因此一定在必要时才使用此关键字。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值