一些Java面试经常提及的知识点

一、static关键字的作用 

1、static修饰成员变量

static修饰的变量属于类,在类初始化时通过类加载器加载到JVM来分配内存空间

2、static修饰成员方法

static修饰的方法属于类方法,不需要创建实例就可以直接调用。在static修饰的成员方法中不能使用this和super等关键字,不能调用非static方法,只能访问所属类的静态成员变量和静态方法

3、static修饰代码块

JVM在加载类时会执行static代码块,static代码块常用于初始化静态变量,static代码块只会在类加载时执行且只执行一次

4、static修饰内部类

static内部类可以不依赖外部类实例对象而被实例化,而内部类需要在外部类实例化后才能实例化

5、static静态导包

用import static 替代import

一般我们导入一个类用

import java.io.File;

而静态导包是这样的

import static java.lang.Integer.*;

 

二、final关键字

1.修饰一个引用

如果引用是一个基本数据类型,则该引用为一个常量,无法修改

如果引用为引用数据类型,比如对象、数组,则该对象、数组本身可以修改,但指向该对象的引用不可以修改

如果引用是类的成员变量,则必须当场赋值,否则编译报错

2、修饰一个方法

当final修饰一个方法时,该方法为最终方法,无法被子类重写。但可以被继承

3.修饰一个类

final修饰类,这个类就是“断子绝孙类”,最终类,无法被继承

 

三、transient关键字

1、如果变量被transient修饰,变量将不会是对象持久化的一部分,该变量内容在序列化后无法获得访问

2、transient关键字只能修饰变量,而不能修饰方法和类。本地变量是不能被transient修饰的

3、被transient修饰的变量不能再被序列化,一个静态变量不管是否被transient修饰均不能被序列化

为什么要使用transient关键字?

当持久化对象时,可能会有一些特殊的对象成员数据,比如银行卡密码等,我们不想序列化来持久化保存它,就在这个域前加上关键字transient即可

四、volatile关键字

一旦一个类的成员变量或者类的静态成员变量被volatile修饰,就具备了两层意思:

1.保证了不同线程对这个变量的操作是可见性的,即一个线程修改了值,那么这个新值对于其他线程是立即可见的

2.禁止进行指令重排序

PS:volatile禁止指令重排序有两层含义:1.当程序执行到volatile变量的读写操作时,该变量前面的操作肯定已经完成,且其结果对后面的操作可见;在该变量后面的操作肯定还没有开始进行。2.在进行指令优化时,不能把volatile变量后面的语句放在其前面执行

volatile关键字不能保证对变量操作的原子性(单线程可以)!!!

volatile的底层实现:

jvm底层采用‘内存屏障’来实现volatile语义。在JMM中,线程之间的通信通过共享内存来实现。

volatile内存语义:

  • 当写一个volatile变量时,JMM会把该线程所对应的本地内存中的共享变量值立即刷新到主内存上

  • 当读一个volatile变量时,JMM会把该线程所对应的本地内存中的共享变量值设为无效,直接从主内存上读

volatile的底层是通过插入内存屏障来实现的,但是对于编译器来说,发现一个最优布置来最小化插入内存屏障的总数几乎是不可能的,所以,JMM采用保守策略。如下:

  • 在每一个volatile写操作前面插入一个StoreStore屏障

  • 在每一个volatile写操作后面插入一个StoreLoad屏障

  • 在每一个volatile读操作后面插入一个LoadLoad屏障

  • 在每一个volatile读操作后面插入一个LoadStore屏障

StoreStore屏障可以保证在volatile写之前,其前面的所有普通写操作都已经刷新到主内存中;
StoreLoad屏障的作用是避免volatile写与后面可能有的volatile读/写操作重排序
LoadLoad屏障用来禁止处理器把上面的volatile读与下面的普通读重排序
LoadStore屏障用来禁止处理器把上面的volatile读与下面的普通写重排序

java中volatile关键字提供了一个功能,那就是被其修饰的变量在被修改后可以立即同步到主内存,被其修饰的变量在每次使用之前都从主内存刷新。因此,可以使用volatile来保证多线程操作时变量的可见性。

五、单例模式

单例模式有差不多6、7种写法吧,这是我感觉比较好用的几种:

public class SingleTon {
	
	private static class SingTonHodler{
		private static SingleTon INSTANCE = new SingleTon();
	}
	
	private SingleTon(){};
	
	public static final SingleTon getInstance(){
		return SingTonHodler.INSTANCE;
	}
}

还有effectiveJava中提倡的一种写法:

public enum SingleTon {
	
	INSTANCE;
	
	public void doSomething(){
		System.out.println("singleTON 创建完成");
	}

}

六、多线程

6.1 线程多次调用start方法,会发生什么?

调用Thread类的start()方法时,此时该线程就处于就绪状态,并没有运行。通过调用run()方法来完成运行操作,run()称为线程体,它包含了要执行的这个线程的内容,run()运行完成,此线程终止。如果CPU再运行其他线程,直接调用run()方法,这只是调用一个方法而已,程序中依然只有一个主线程,是没有达到多线程的目的的。

一个线程对象只能调用一次start()方法,如果调用多次就会抛出java.lang.IllegalThreadStateException。

可以被重复调用的是Run()方法。

6.2 常用的几种线程池

打开java.util.concurrent.Executors类,可以看到的线程池列表如下:

但常用的也就几种。

6.2.1 newCachedThreadPool 

这是一个可缓存的线程池,如果线程池个数超过需要的个数,可灵活回收空闲线程;如若不够,则创建新线程。

这种线程池的特点是:

1.创建的线程最大个数为Interger. MAX_VALUE,相当于没有限制

2.如果长时间没有向线程池中提交任务——工作线程空闲了指定时间(默认1分钟),则工作线程将自动终止。这时再提交一个新任务,线程池会重新创建一个新的线程。

3.使用该线程时,一定要注意控制线程池线程的数量,否则大量线程同时运行会造成系统瘫痪。

6.2.2 newFixedThreadPool

这是一个创建固定工作线程数量的线程池。每提交一个任务就会创建一个工作线程,如果任务超过了线程池的线程数量,则将超过数量的任务存入池队列中。

FixedThreadPool是一个典型且优秀的线程池。它具有线程池提高程序效率和节省创建线程所耗开销的优点。但是在线程池空闲时,即线程池中没有可运行的任务时,它也不会释放工作线程,还会占用一定的系统资源。

该线程池的大小最好根据系统资源进行设置:Runtime.getRuntime().availableProcessors();

6.2.3 newSingleThreadExecutor

这是一个创建单线程的Executor,即只创建唯一的工作者线程来执行任务,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。如果这个线程异常结束,会有另一个取代它,保证顺序执行。单工作线程最大的特点是可保证顺序地执行各个任务,并且在任意给定的时间不会有多个线程是活动的。

6.2.4 newScheduleThreadPool

这是一个创建定时工作线程的线程池。支持定时执行和周期性执行。

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值