-
为什么Java被称作是“平台无关的编程语言”?
java源程序
先经过javac编译器
编译成二进制的.class字节码文件
,.class文件再运行在jvm上,java解释器
(jvm的一部分)会将其解释成对应平台的机器码
执行。所以java所谓的跨平台就是在不同平台上安装了不同的jvm,而在不同平台上生成的.class文件都是一样的。 -
是否可以在static环境中访问非static变量?
Static
表示静态的意思,可用于修饰成员变量和成员函数。因为静态的成员属于类,随着类的加载而加载到静态方法区内存,当类加载时,此时不一定有实例创建,没有实例,就不可以访问非静态的成员。同理,Java中static方法不能被覆盖,因为方法覆盖是基于运行时动态绑定的。 -
Java支持的数据类型有哪些?什么是自动拆装箱?
Java支持的数据类型包括两种
- 基本数据类型:包含byte,char,short, boolean ,int , long, float,double
- 引用类型:如String等,JVM中虚拟栈中存的是对象的地址,创建的对象实质在堆中,通过地址来找到堆中的对象的过程,即为引用类型。
自动装箱就是Java编译器在基本数据类型和对应的对象包装类型间的转化,即int转化为Integer,自动拆箱是Integer调用其方法将其转化为int的过程。
-
Java中的方法重写和方法重载是什么意思?
方法重写的原则:
- 重写方法的方法名称、参数列表必须与原方法的相同,返回类型可以相同也可以是原类型的子类型(从Java SE5开始支持)。
- 重写方法不能比原方法访问性差(即访问权限不允许缩小)。
- 重写方法不能比原方法抛出更多的异常。
- 被重写的方法不能是final、private和static。
- 重写是发生在运行时的,因为编译期编译器不知道并且没办法确定该去调用哪个方法,JVM会在代码运行的时候作出决定。
方法重载的原则:
- 方法名称必须相同。
- 参数列表必须不同(个数不同、或类型不同、参数类型排列顺序不同等)。
- 方法的返回类型可以相同也可以不相同,仅仅返回类型不同不足以成为方法的重载。
- 重载是发生在编译时的,因为编译器可以根据参数的类型来选择使用哪个方法。
重写和重载的不同:
- 方法重写要求参数列表必须一致,而方法重载要求参数列表必须不一致。
- 方法重写要求返回类型必须一致(或为其子类型),方法重载对此没有要求。
- 方法重写只能用于子类重写父类的方法,方法重载用于同一个类中的所有方法。
- 方法重写对方法的访问权限和抛出的异常有特殊的要求,而方法重载在这方面没有任何限制。
- 父类的一个方法只能被子类重写一次,而一个方法可以在所有的类中可以被重载多次。
-
接口和抽象类的区别是什么?
不同点在于:
- 接口中所有的方法隐含的都是抽象的。而抽象类则可以同时包含抽象和非抽象的方法。
- 类可以实现很多个接口,但是只能继承一个抽象类
- 类可以不实现抽象类和接口声明的所有方法,当然,在这种情况下,类也必须得声明成是抽象的。
- 抽象类可以在不提供接口方法实现的情况下实现接口。
- Java接口中声明的变量默认都是final的。抽象类可以包含非final的变量。
- Java接口中的成员函数默认是public的。抽象类的成员函数可以是private,protected或者是public。
从设计层面来说,抽象是对类的抽象,是一种模板设计,接口是行为的抽象,是一种行为的规范。
-
值传递和引用传递?
参见《Head first JAVA》的杯子和遥控器模型。 -
进程和线程的区别是什么?
- 进程是运行中的程序,线程是进程的内部的一个执行序列
- 进程是资源分配的单元,线程是执行行单元
- 进程间切换代价大,线程间切换代价小
- 进程拥有资源多,线程拥有资源少
- 多个线程共享进程的资源,进程间地址和资源独立
守护线程:服务线程,是指在程序运行的时候在后台提供一种通用服务的线程,比如垃圾回收线程,并且这种线程并不属于程序中不可或缺的部分。当所有的非守护线程结束时,程序也就终止了,同时会杀死进程中的所有守护线程。
-
创建线程有几种不同的方式?
创建方式:
- 继承Thread类(真正意义上的线程类),是Runnable接口的实现。
- 实现Runnable接口,并重写里面的run方法。
- 使用Executor框架创建线程池。Executor框架是juc里提供的线程池的实现。
Thread类的常见方法参见:创建线程有几种不同的方式?
- 线程的几种可用状态
- 新建( new ):新创建了一个线程对象。
- 就绪状态( runnable ):线程对象创建后,其他线程调用了该对象的 start()方法。该状态的线程位于可运行线程池中,等待被线程调度选中获 取 cpu 的使用权。
- 运行( running ):已就绪的线程获得了 cpu 时间片( timeslice ),执行程序代码。
- 阻塞( block ):阻塞状态是指线程因为某种原因放弃了 cpu 使用权,暂时停止运行。阻塞的情况分三种:
(一). 等待阻塞:运行的线程执行 o.wait ()方法, JVM 会把该线程放 入等待队列中。
(二). 同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则 JVM 会把该线程放入锁池中。
(三). 其他阻塞: 运行的线程执行 Thread.sleep ( long ms )或 t.join ()方法,或者发出了 I / O 请求时, JVM 会把该线程置为阻塞状态。 - 死亡( dead ):线程 run ()、 main () 方法执行结束,或者因异常退出了 run ()方法,则该线程结束生命周期。死亡的线程不可再次复生。
-
如何确保N个线程可以访问N个资源同时又不导致死锁?
死锁是指多个进程因竞争资源而造成的一种僵局(互相等待),若无外力作用,这些进程都将无法向前推进。多线程产生死锁的四个必要条件:
- 互斥条件:一个资源每次只能被一个进程使用。
- 保持和请求条件:一个进程因请求资源而阻塞时,对已获得资源保持不放。
- 不可剥夺:进程已获得资源,在未使用完成前,不能被剥夺。
- 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
只要破坏其中任意一个条件,就可以避免死锁,其中最简单的就是破环循环等待条件。按同一顺序访问对象,加载锁,释放锁。
- 线程池是什么?
-
创建线程池的方式
①newFixedThreadPool(int nThreads)
:创建固定长度线程池,每当提交一个任务就创建一个线程,直到达到线程池的最大数量。
②newCachedThreadPool()
:创建一个可缓存的线程池,线程池的规模不存在任何限制。如果线程池的规模超过了处理需求,将自动回收空闲线程,而当需求增加时,则可以自动添加新线程。
③newSingleThreadExecutor()
:创建单个工作线程来执行任务,特点是能依照任务在队列中的顺序来串行执行。
④newScheduledThreadPool(int corePoolSize)
:创建固定长度线程池,而且以延迟或定时的方式来执行任务,类似于Timer。 -
线程池状态
状态转换图
线程池的5种状态:RUNNING、SHUTDOWN、STOP、TIDYING、TERMINATED。
①RUNNING
:线程池一旦被创建,就处于 RUNNING 状态,任务数为 0,能够接收新任务,对已排队的任务进行处理。
②SHUTDOWN
:不接收新任务,但能处理已排队的任务。
③STOP
:不接收新任务,不处理已排队的任务,并且会中断正在处理的任务。
④TIDYING
:SHUTDOWN 或STOP状态下,任务数为 0, 其他所有任务已终止,线程池会变为 TIDYING 状态。
⑤TERMINATED
:线程池在 TIDYING 状态执行完 terminated() 方法就会彻底终止。
-
线程池中
submit()
和execute()
二者都是用于提交任务,区别在于
①接收参数:execute()
方法只能接收实现Runnable
接口类型的任务;submit()
方法则既可以接收Runnable
和Callable
类型的任务。
②返回值:execute()
的返回值是void
;submit()
的返回值是Future
,通过.get()
方法可以获取到线程执行的返回值。
③异常的处理:execute()
执行Runnable
的任务时,run()方法有显式的抛出异常。
- 同步方法和同步代码块的区别是什么?
-
同步的意义
java允许多线程并发控制,当多个线程同时操作一个可共享的资源变量时(增删改查),将会导致数据的不准确,相互之间产生冲突。因此加入了同步锁,以避免在该线程没有结束前,调用其他线程。从而保证了变量的唯一性,准确性。 -
同步的实现:在 java 虚拟机中, 每个监视器和一个对象引用相关联, 每个对象都关联着一把锁。如果使用关键字
synchronized
修饰方法的方法或者代码块,那么这个部分将获取内置锁,放入了监视器的监视区域, 确保只有当前线程可以访问该变量,其他线程被阻塞住。 -
注意:
(一). 同步方法默认用this或者当前类class对象作为锁。
(二). 若同步静态方法,调用过程,将锁住整个类。
(三). 同步代码块可以选择以什么来加锁,比同步方法要更颗粒化
- 怎么保证多线程的运行安全
- 线程安全在三个方面体现
① 原子性:提供互斥访问,同一时刻只能有一个线程对数据进行操作(atomic, synchronized);
② 可见性:一个线程对主内存的修改可以及时地被其他线程看到(synchronized, volatile);
③ 有序性:一个线程观察其他线程中的指令执行顺序,由于指令重排序,该观察结果一般杂乱无序(happens-before原则)。 - ThreadLocal实现线程安全
ThreadLocal类来支持线程局部变量,线程局部变量是局限于线程内部的变量,属于线程自身所有,不在多个线程间共享。任何线程局部变量一旦在工作完成后没有释放,Java 应用就存在内存泄露的风险。
- synchronized、volatile、Lock、ReentrantLock、atomic
-
synchronized
和volatile
区别
① volatile仅有可见性,不能保证原子性;synchronized具有可见性和原子性
② volatile仅能使用在变量级别;synchronized则可以使用在变量、方法、和类。
③ volatile不会造成线程的阻塞;synchronized可能会造成线程的阻塞。
④ volatile标记的变量不会被编译器优化;synchronized标记的可以被优化 -
synchronized
和Lock
区别
① synchronized是java内置关键字,在jvm层面,Lock是个java类;
② synchronized无法判断是否获取锁的状态,Lock可以判断是否获取到锁;
③ synchronized会自动释放锁,Lock需在finally中手工释放锁;
④ synchronized如果阻塞会一直保持等待,而Lock如果尝试获取不到锁,线程可以不用一直等待就结束;
⑤ synchronized的锁可重入、不可中断、非公平,而Lock锁可重入、可判断、可公平(两者皆可);
⑥ Lock锁适合大量同步的代码,synchronized锁适合代码少量的同步问题。 -
synchronized
和ReentrantLock
区别
二者的本质区别在于synchronized是关键字,ReentrantLock是类,因此ReentrantLock提供了更多更灵活的特性,可以被继承、可以有方法、可以有各种各样的类变量。
网友评论