小言_互联网的博客

java常用关键字总结

287人阅读  评论(0)

Java中的关键字很多,本文选取常用的几个进行学习总结;

static


一、基本概念

静态,全局的,一旦被修饰,说明被修饰的东西在一定范围内是共享的,这时候要注意并发读写问题;

可以用来修饰类变量方法代码块

1、static修饰类变量----类属性(静态属性)

static修饰的类变量无需初始化,通过 类名.变量 直接访问;这时候要注意线程安全问题

当多个线程对共享变量进行读写时,会出现并发问题;譬如定义了 public static List<String> list = new ArrayList();这时候有两个解决办法,一个是把线程不安全的ArrayList换成线程安全的CopyOnWriteArrayList;另一种就是每次访问时手动加锁;

2、static修饰方法----类方法(静态方法)

通过 类名.方法名 直接访问;注意被static修饰的方法内部只能调用static修饰的方法,不能调用普通方法

我们常用的Utils类中的方法习惯用static修饰,一方面是简单方便,另一方面是因为被static修饰的方法不用担心线程安全问题;

3、static修饰代码块

又称静态代码块;常用于在类启动前,初始化一些值;

需要注意的是,静态代码块只能调用被static修饰的变量,并且static变量需要写在静态代码块的前面,不然会报错;

二、加载时机

打印出来的结果如下:

父类的静态变量初始化
父类静态块初始化
子类静态变量初始化
子类静态块初始化
父类构造器初始化
子类构造器初始化

结论:

父类的静态变量和静态块比子类的静态变量和静态块优先加载

静态块和静态变量比类构造器优先初始化;

静态方法在类初始化的时候不会初始化,只有在调用的时候才会初始化

 

final


一般情况下,final用于以下场景:

  • final修饰的类不能被继承;
  • final修饰的方法不能被重写;
  • final修饰的变量在声明的时候就必须初始化,而且不能再修改其内存地址;

 

try catch finally


这些关键字用于异常捕获流程中,其中:

try用来确定代码执行的范围,catch用来捕获可能发生的异常,finally用来执行无论如何一定要执行的代码;

特别需要注意的时,当try和catch都遇到异常时,代码的执行顺序:try-->catch-->finally:

public void testCatchFinally() {
  try {
    log.info("try is run");
    if (true) {
      throw new RuntimeException("try exception");
    }
  } catch (Exception e) {
    log.info("catch is run");
    if (true) {
      throw new RuntimeException("catch exception");
    }
  } finally {
    log.info("finally is run");
  }
}

输出结果如下图:

可以看到,

finally执行完成后,才抛出catch异常;最终捕获的是catch的异常,try抛出来的异常会被catch吃掉, 所以当catch也有可能抛出异常时,先打印try的异常,从而try的异常可以再日志中有所体现

 

volatile


原本意思是可见的,

当一个变量定义为volatile之后,它将具备两种特性——保持内存可见性和防止指令重排。

内存可见性

在Java中,用来修饰共享变量,当共享变量的值发生变化时,会及时通知其他线程,从而知道当前的共享变量值已经被修改;

这里要注意,随着技术的迭代,现在的计算机多为多核cpu,多核cpu情况下,为了提高效率,线程在获取值的时候,都是优先从cpu缓存区获取,若cpu缓存中没有,才去内存中获取,所以线程读的操作都是从cpu缓存区获取,由于cpu缓存区和内存的值并不总是同步的,这就引出了一个机制:当内存中共享变量的值发生变化时,内存会主动通知cpu缓存,当前共享变量的值已失效,需要重新从内存中拉取共享变量的值;volatile关键字就会触发这种机制;

被volatile修饰的变量,会被识别成共享变量,当内存中的值被修改后,会通知所有的cpu缓存,使cpu缓存中的值也对应修改,从而保证线程从cpu缓存中获取的值是最新的值;

防止指令重排

常用的单例模式就用到了volatile关键字:

public class Singleton {
    private volatile static Singleton instance; //声明成 volatile
    private Singleton (){}
    public static Singleton getSingleton() {
        if (instance == null) {                         
            synchronized (Singleton.class) {
                if (instance == null) {       
                    instance = new Singleton();
                }
            }
        }
        return instance;
    } 
}

代码中,instance=new Singleton();这条语句并不是一个原子操作:

1.分配内存给对象,在内存中开辟一段地址空间;// Singleton var = new Singleton();
2.对象的初始化;// var = init();
3.将分配好对象的地址指向instance变量,即instance变量的写;// instance = var;
如果不使用volatile关键字限制指令的重排序,1-3-2操作,获得到单例的线程可能拿到了一个空对象,后续操作会有影响!因此需要引入volatile对变量进行修饰;
 

default


一般用在接口的方法上,表示对于该方法,子类是无需强制实现的,但自己必须有默认实现;

 

this


this调用本类属性:只要在类中访问类的属性,一定要加上this关键字;

this表示当前对象;只要对象调用了本类中的方法,这个this就表示当前执行的对象;

this调用本类方法;

譬如简单的java pojo中,代码如下:

public class Person {
	private String name;
	private Long age;
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public Long getAge() {
		return age;
	}
	public void setAge(Long age) {
		this.age = age;
	}
	public Person(String name, Long age) {
		super();
		this.name = name;
		this.age = age;
	}
}

 

super


 使用super调用父类的属性和方法;具体代码如下:

class Person{
    public String info = "爸爸!";
    public void print(){
        System.out.println("I am father");
    }
}

class Student extends Person{
    public String info = "儿子!" ;
    public void print(){
    	//调用父类的方法
        super.print();
        System.out.println("I am child");
        //调用父类中的属性
        System.out.println(super.info);
        System.out.println(this.info);
    }
}

public class Test {
	 public static void main(String[] args) {
	       Student student= new Student();
	       student.print();
	 }
}

 

 throws


 用在方法上,表示方法可能会产生异常但是方法内部不处理,将异常抛回给调用处

public class Demo {
    public static void main(String[] args) {
        try {
            System.out.println(calculate(10, 0));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
   
    public static int calculate(int x,int y) throws Exception {
        return x/y ;
    }
}

运行结果如下:

 

throw


用在方法中,表示异常对象由用户产生而非JVM产生,一般与自定义异常类搭配使用

public static void main(String[] args){
        try {
            throw new Exception("抛个异常玩玩!") ;
        } catch (Exception e) {
            e.printStackTrace();
        }
}

 

synchronized


Java的一种同步锁机制;

修饰代码块

class SyncThread implements Runnable {
	   private static int count;
	   public SyncThread() {
	      count = 0;
	   }
	   public  void run() {
		   //同步代码块
	      synchronized(this) {
	         for (int i = 0; i < 5; i++) {
	            try {
	               System.out.println(Thread.currentThread().getName() + ":" + (count++));
	               Thread.sleep(100);
	            } catch (InterruptedException e) {
	               e.printStackTrace();
	            }
	         }
	      }
	   }
	   public int getCount() {
	      return count;
	   }
	}

	public class Demo1{
	    public static void main(String[] args) {
	    	//runnable 
	        SyncThread syncThread = new SyncThread();
	        //thread1
	        Thread thread1 = new Thread(syncThread, "SyncThread1");
	        //thread2
	        Thread thread2 = new Thread(syncThread, "SyncThread2");
	        thread1.start();
	        thread2.start();
	    }
	}

此时,Thread1和thread2是互斥的,因为在执行synchronized代码块时会锁定当前的对象,只有执行完该代码块才能释放该对象锁,下一个线程才能执行并锁定该对象,执行记过如下:

SyncThread1:0
SyncThread1:1
SyncThread1:2
SyncThread1:3
SyncThread1:4
SyncThread2:5
SyncThread2:6
SyncThread2:7
SyncThread2:8
SyncThread2:9

现在,把SyncThread的调用稍微改一下:

SyncThread syncThread1 = new SyncThread();
SyncThread syncThread2 = new SyncThread();
Thread thread1 = new Thread(syncThread1, "SyncThread1");
Thread thread2 = new Thread(syncThread2, "SyncThread2");
thread1.start();
thread2.start();

执行结果如下:

SyncThread1:0
SyncThread2:1
SyncThread1:2
SyncThread2:2
SyncThread2:4
SyncThread1:3
SyncThread2:5
SyncThread1:6
SyncThread1:7
SyncThread2:8

修饰普通方法

被synchronized修饰的方法称为同步方法,其作用的范围是整个方法,作用的对象是调用这个方法的对象;

public synchronized void run() {
   for (int i = 0; i < 5; i ++) {
      try {
         System.out.println(Thread.currentThread().getName() + ":" + (count++));
         Thread.sleep(100);
      } catch (InterruptedException e) {
         e.printStackTrace();
      }
   }
}

修饰静态方法

修改一个静态的方法,其作用的范围是整个静态方法,作用的对象是这个类的所有对象;

class SyncThread implements Runnable {
   private static int count;
 
   public SyncThread() {
      count = 0;
   }
 
   public synchronized static void method() {
      for (int i = 0; i < 5; i ++) {
         try {
            System.out.println(Thread.currentThread().getName() + ":" + (count++));
            Thread.sleep(100);
         } catch (InterruptedException e) {
            e.printStackTrace();
         }
      }
   }
 
   public synchronized void run() {
      method();
   }
}

public class Test{
    public static void main(String[] args) {
       SyncThread syncThread1 = new SyncThread();
	   SyncThread syncThread2 = new SyncThread();
       Thread thread1 = new Thread(syncThread1, "SyncThread1");
       Thread thread2 = new Thread(syncThread2, "SyncThread2");
       thread1.start();
       thread2.start();
    }
}

修饰类

修饰类,其作用的范围是synchronized后面括号括起来的部分,作用主的对象是这个类的所有对象

class ClassName {
   public void method() {
      synchronized(ClassName.class) {
         // todo
      }
   }
}

 

常见面试题:


1 常常看见变量和方法被final和static两个关键字修饰,为什么?

变量和方法与对象无关,属于类成员,从而可以直接类名.变量、类名.方法使用,简单方便;
强调变量内存地址不可变,方法不可被重写,强调方法内部的稳定性;

2 catch中发生了未知异常,finally还会执行吗?

catch中发生了异常,finally还是会执行,
并且当finally执行完成后,才会抛出catch异常

3 throw 和throws的区别

throw用于方法内部,主要表示手工抛出异常;
throws主要在方法声明上使用,明确告诉用户本方法可能产生的异常,同时该方法可能不处理

 


转载:https://blog.csdn.net/wuyundong123/article/details/100984407
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场