0%

Java关键字

在Java这门面向对象编程语言中,有几个关键字是必须了解的,下面是我在学习的记录笔记。

static

在平时,我们调用一个类里面的属性或者方法的时候,需要new一个新的类,然后调用类的方法或者属性。
static表示全局或者静态的意思。可用来修饰成员变量或者方法。被static修饰的成员变量和方法独立于该类的任何对象,也就是说,它不依赖类特定的实例,就是被这个修饰的不需要实例化就可以使用,就可以被类的所有实例共享。
static代码块也叫静态代码块,在类中独立于类成员的static语句块,JVM加载时 会执行这些静态代码块。
在静态方法中,不能访问非静态方法或者是非静态变量。如代码中,准确的来说,只有被static修饰的变量和方法,才能不需要实例化直接使用,即使在static修饰的方法中,也不能直接调用没有实例化的非静态方法或者是非静态变量。

final

一旦使用了final,该变量就不可修改;如果方法被final修饰,那么这个方法就不可被子类重写;final类,是不能被子类继承的。这里有一篇博客讲的挺详细的。

  • final 修饰类,表示该类不可以被继承
  • final 修饰变量,分两种情况,如果修饰的是基本类型变量,那么只能被赋值一次,不能被赋值两次, 如果修饰的是引用类型变量,那么引用指向的内存地址将不可变,但是引用类型内的属性可以被修改
  • final 修饰方法,表示该方法不可以被子类重写,但是可以被子类继承使用

final的好处:

  • final关键字提高了性能。JVM和java的应用都会缓存final变量
  • final变量可以安全的在多线程环境下进行共享;
  • 使用final关键字,JVM会对方法、变量及类进行优化。

final修饰的变量是引用类型变量,那么引用指向的内存地址将不可变,但是引用类型内的属性可以被修改。

1
2
3
final StringBuffer a = new StringBuffer("hello");
a.append(" world");
System.out.println(a);

abstract

抽象类里面不一定有抽象方法,但是有抽象方法,那么这个类就是抽象类。
使用abstract关键字修饰的话,类可以进行扩展,但是方法不能实例化,如下面的代码。

1
2
3
public abstract class AbstractTest {
public abstract void test();
}

虽然抽象方法不能实例化,但是可以通过继承,在子类中进行重写。其实Java中接口interface就是一个抽象类。

1
2
3
4
5
6
public class MainTest extends AbstractTest{  
@Override
public void test() {
System.out.println("Hell abstract");
}
}

顺便我们来下看接口类的方法中,我们咋样来写方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public interface interfaceTest {

int a = 3;
final static int b = 4;
//这种是Java8中新增的一种方法
default int sum() {
return 5;
}

static int sum(int a, int b) {
return 4;
}

int sum(int a);
//抽象方法可以实现
abstract int sum(int a, float b);
}

volatile

volatile则是轻量级的synchronized。如果一个变量使用volatile,则它比使用synchronized的成本更加低,因为它不会引起线程上下文的切换和调度。Java语言规范对volatile的定义如下:

Java编程语言允许线程访问共享变量,为了确保共享变量能被准确和一致地更新,线程应该确保通过排他锁单独获得这个变量

1
i = i + 1;

对于这语句,在执行的过程中,会先从内存中读取i的值,然后复制一份到高速缓存中,然后CPU执行指令对进行加1操作,然后将数据写入高速缓存,最后将高速缓存中最新的值刷新到主存中

volatile保证了下面这两点:

  • 可见性
  • 禁止重排序

Java提供volatile关键字来保证可见性。当一个共享变量被修饰的时,它会保证修改的值会立即被更新到主存,当有其他线程需要读取时,它会去内存中读取新值。
volatile变量规则:对一个变量的写操作先行发生于后面对这个变量的读操作。
volatile修饰符组织了变量访问前后的指令重排,保证指令执行顺序,但是不能保证线程安全,因为不能保证原子性。
下面的代码起了20个线程,每个线程对race自增10000,理论上应该是200000,但是最后输出的结果并不是,而且每次运行都不一样。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class VolatileTest {

private static volatile int race = 0;

public static void increase() {
race++;
}

public static void main(String[] args) {

Thread[] threads = new Thread[20];
for (int i = 0; i < 20; i++) {
threads[i] = new Thread(new Runnable() {
@Override
public void run() {
for (int j = 0; j < 10000; j++) {
increase();
}
}
});
threads[i].start();
}
//等所有累加线程结束
while (Thread.activeCount() > 1)
Thread.yield();

System.out.println(race);
}
}

this

this 只能在方法内使用,表示对调用的方法的对象的引用。

transient

如果一个对象中的某个属性不希望被序列化,则可以使用transient关键字进行声明。

客官,赏一杯coffee嘛~~~~