1、用最有效率的方法计算2乘以8?
2 << 3(左移3位相当于乘以2的3次方,右移3位相当于除以2的3次方)。
2、数组有没有length()方法?String有没有length()方法?
数组没有length()方法,有length 的属性;String有length()方法;集合使用的是size()方法。
3、构造器(constructor)是否可被重写(override)?
public final class Constructor<T> extends Executable
由于被final关键字修饰,所以不能被继承,进而导致不能被重写,但是可以被重载。
4、String和StringBuffer、StringBuilder的区别?
- String:被final修饰,所以长度不可变,底层使用的是数组(private final char value[]);
- StringBuffer:JDK1.0就有,长度可变,线程安全,效率低,继承自AbstractStringBuilder;
- StringBuilder:jdk1.5引入进来的,长度可变,线程不安全,效率高,继承自AbstractStringBuilder;
- StringBuffer和StringBuilder里面的方法作用都相同,不同之处在于StringBuffer中的每个方法都有synchronized关键字修饰;
- 三者都是final类,都不能被继承。
5、字符串拼接为什么不建议使用“+”
因为String长度不可变,使用“+”拼接,每次拼接都会产生一个新的对象,这样会有大量对象产生,造成内存浪费;
通常情况下如果不要求线程的安全性,使用StringBuilder的append()方法进行字符串拼接;
6、单例设计模式中的懒汉式和饿汉式有什么区别
饿汉式:
package singleton;
/**
* 饿汉式单例
* 特点:类一旦加载就创建一个单例,保证在调用getInstance方法之前单例已经存在了
*/
public class HungrySingleton {
private static final HungrySingleton instance = new HungrySingleton();
//私有构造,避免外部初始化
private HungrySingleton() {
}
//静态公有方法,获取该实例对象
public static HungrySingleton getInstance() {
return instance;
}
}
懒汉式第一种:
package singleton;
/**
* 懒汉式单例
* 特点:只有在调用getInstance方法时才会创建实例对象
*/
public class LazySingleton {
private static LazySingleton instance = null;
private LazySingleton() {
}
/**
* 问题:
* 使用synchronized关键字修饰,可以保证线程安全性;
* 但同时有synchronized关键字修饰,所以该多线程访问时,都要判断锁,会导致效率较低
*/
public static synchronized LazySingleton getInstance() {
if (instance == null) {
instance = new LazySingleton();
}
return instance;
}
}
懒汉式第二种:
package singleton;
/**
* 懒汉式更好的解决方案:使用双判断的方式
*/
public class Lazy {
public static Lazy instance = null;
private Lazy() {
}
/**
* 使用双重判断的方式来解决懒汉式的效率问题,即当一个线程创建了该实例对象,以后的线程访问的时候该对象 * 就不为null,既不用再去判断锁了,这样可以减少判断锁的次数,提高了效率
*/
public static Lazy getInstance() {
if (instance == null) {
//静态方法的锁使用的是该静态方法所在类的class文件对象
synchronized (Lazy.class) {
if (instance == null) {
instance = new Lazy();
}
}
}
return instance;
}
}
1.懒汉式和饿汉式的区别:懒汉式的特点在于实例的延迟(所以是懒汉式)加载;
2.懒汉式有什么问题:如果多线程访问时会出现安全问题,可以加同步(synchronized)来解决,但效率较低;
3.怎么解决懒汉式效率低的问题:可以使用双重判断可以解决这个问题;
4.加同步的时候,使用的锁是哪一个:该类所属的字节码文件对象(静态)。
7、synchronized关键字
修饰非静态方法:使用的锁是this;
public synchronized void method() {}//有synchronized关键字修饰就是同步的
修饰代码块:使用的锁由自己决定,只要是对象就可以;
Object obj = new Object()
synchronized (obj){
//......
}
修饰静态方法:使用的锁是该方法所属类的字节码文件。
public static synchronized void sale() {}
8、Java中String直接初始化赋值和new的区别
1.初始化:
String s2 = "abc";
2.new出来:
String s1 = new String("abc");
3.区别:
- 初始化可能创建一个或者不创建对象,如果”abc”这个字符串在String池中不存在,则会在String池中创建一个对象,无论以后用这种方式(初始化方式)创建多少个值为”abc”的字符串对象,始终只有一个内存地址被分配,之后的都是String的拷贝,Java中称为“字符串驻留”;
- new出来的至少创建一个对象,也可能两个。因为用到new关键字,肯定会在堆中创建一个对象,它的值是“abc”,同时如果“abc”在String池里不存在,还会在java池里创建这个String对象“abc”。
4.垃圾回收:
在JVM里,考虑到垃圾回收(Garbage Collection)的方便,将heap(堆)划分为三部分:
young generation(新生代)
old generation(旧生代)
permanent generation(永生代)
字符串为了解决字符串重复问题,生命周期长,存于permanent generation中(永生代)
5.代码:
package demo;
public class Demo {
public static void main(String[] args) {
String s1 = new String("abc");
String s2 = "abc";
String s3 = new String("abc");
String s4 = "abc";
System.out.println(s1.equals(s2));//true
System.out.println(s1 == s2);//false
System.out.println(s1 == s3);//false
System.out.println(s2 == s3);//false
System.out.println(s2 == s4);//true
}
}
9、多线程实现的两种方式
第一种:继承Thread类;
第二种:实现Runnable接口;
区别:
实现Runnable接口的方式可以避免由于Java单继承带来局限性,Java语言中只能继承一个类,但是可以实现多个接口,所以使用实现Runnable接口的方式更为灵活。
10、如何使用多线程
- 继承Thread类或实现Runnable接口;
- 重写run()方法;
- 掉用start()方法开启线程。
11、线程状态(线程的生命周期)
注意:冻结状态又可分为睡眠状态和等待状态
12、访问修饰符public等的区别?
修饰符 | 当前类 | 同 包 | 子 类 | 其他 |
---|---|---|---|---|
public | √ | √ | √ | √ |
protected | √ | √ | √ | × |
default | √ | √ | × | × |
private | √ | × | × | × |
13、String 是最基本的数据类型吗?
Java中的基本数据类型只有8个:byte、short、int、long、float、double、char、boolean;
除了基本类型(primitive type)和枚举类型(enumeration type),剩下的都是引用类型(reference type)。
14、Java基本数据类型转换
类型: | 字节: | 范围: | 默认值: |
---|---|---|---|
byte | 1 | -128~127 | 0 |
short | 2 | -32768~32767 | 0 |
char | 2 | 0~65535 | '\u0000' |
int | 4 | -2147483648~2147483647 | 0 |
long | 8 | -2^63~2^63-1 | 0L或0l |
float | 4 | 3.402823e38~1.401298e-45 | 0.0F或0.0f |
double | 8 | 1.797693e308~4.9000000e-324 | 0.0D或0.0d |
boolean | 1 | false 或 true | false |
基本数据类型的精度(亦称容量大小)排序为(从小到大):(byte->short->char)->int->long->float->double;
向下转换:高精度向低精度转换,需要强制转换,可能会导致精度损失;
向上转换:低精度向高精度转换,一般由编译器完成,当几个基本数据类型参与运算时,计算结果的类型是参与运算中精度最高的那个运算数的类型,其他运算数的类型将被自动转为精度最高的那个运算数的类型;
之所以要给byte,short,char三个类型加上括号,是因为,当它们参与运算时首先会被提升为int类型,也就说明他们之间不会发生隐式类型转换。
15、switch (expr)
expr可以是byte、short、int、char、enum、String类型,但是long类型不能
16、内存中栈(stack)、堆(heap)和静态区(static area)的用法
栈空间操作起来最快但是栈很小,通常大量的对象都是放在堆空间;
String str = new String("hello");
变量str放在栈上,用new创建出来的字符串对象放在堆上,而”hello”这个字面量放在静态区。
17、重载和重写的区别
重载:发生在一个类中,方法名相同,参数列表不同(参数类型不同、参数个数不同甚至是参数顺序不同);
重写:发生在父类与子类之间,方法名、参数列表、返回值类型相同,子类方法的访问修饰符权限不能严于父类,子类抛出的异常范围小于父类。
18、接口和抽象类的区别
参数 | 抽象类 | 接口 |
---|---|---|
默认的方法实现 | 可以有默认的方法实现 | 不存在方法的实现 |
实现 | 子类需要实现抽象类的所有抽象方法,否则继续抽象 | 需要实现接口中的所有方法 |
构造函数 | 可以有,但是不能实例化对象 | 不能有 |
访问修饰符 | public、protected、default、private都可以 | 默认修饰符是public |
main方法 | 可以有main方法 | 没有main方法,jdk1.8以后可有 |
- 抽象类和接口都不能够实例化;
- 接口中的变量都默认是public static final的全局常量;
- 有抽象方法的类必须被声明为抽象类,而抽象类可以没有抽象方法。
19、接口是否可继承接口?抽象类是否可实现接口?抽象类是否可继承具体类?
- 接口可以继承接口,而且支持多重继承;
- 抽象类可以实现接口;
- 抽象类可以继承具体的类也可以继承抽象类。
20、如何将字符串转换为基本数据类型?如何将基本数据类型转换为字符串?
package demo02;
public class StringToBasic {
/**
* 字符串转基本数据类型:
* 调用基本数据类型对应的包装类中的方法parseXxx();
* 注意:如果字符串为"abc",则无法转换,因为Character没有提供parseXxx()方法。
*/
private static void stringToBasic() {
String s = "123";
int i = Integer.parseInt(s);
double d = Double.parseDouble(s);
System.out.println(i);//123
System.out.println(d);//123.0
}
/**
* 基本数据类型转换为字符串:
* 1.将基本数据类型与空字符串("")使用+连接即可获得其所对应的字符串(常用);
* 2.调用String类中的valueOf()方法返回相应字符串;
* 3.如果基本数据类型使用的是其包装类,则可直接调用toString()方法。
*/
public static void basicToString() {
int i = 100;
String s1 = i + "";//第一种方法
String s2 = String.valueOf(i);//第二种方法
System.out.println(s1);
System.out.println(s2);
Integer b = 99;
String s3 = b.toString();//第三种方法
System.out.println(s3);
}
}
21、递归实现字符串反转
/**
* 递归实现字符串反转:
* substring(i)用于返回数组下标为i及之后的元素
* charAt(i)用于返回数组下标为i的元素
*
* @param originStr
* @return
*/
public static String reverse(String originStr) {
if (originStr == null || originStr.length() <= 1) {
return originStr;
}
return reverse(originStr.substring(1)) + originStr.charAt(0);
}
22、Object类中的方法
Object是所有类的父类,任何类都能继承自Object,Object中有如下方法,也就是说所有的类都有如下方法:
clone():实现Cloneable接口才能调用此方法,实现对象的浅拷贝;
getClass():final方法,不能被覆写,获得方法所在类运行时类型;
toString():一般都会覆写该方法,使该对象以字符串形式输出;
finalize():用于释放资源,较少使用;
equals():注意区别equals和==的不同,在Object中是一样的,都是用于比较对象的地址是否相同。但子类一般会覆写该方法,String类已覆写了该方法,覆写过后equals一般用于比较内容,==用于比较地址。
hashCode():一般覆写equals的同时都要覆写hashCode方法;
wait():使当前线程等待该对象的锁;
notify():唤醒在该对象上等待的某个线程;
notifyAll():唤醒在该对象上等待的所有线程。
23、Java异常分类
非运行时异常(检查异常、编译异常):指的是在编写代码时JVM提示的异常(JVM编译都不能通过),比如:IOException、SQLException,必须手动抛出或捕获;
运行时异常(非检查异常):程序运行时出现的异常,比如:
ArithmeticException(算术运算异常);
ClassCastException (类型转换异常);
IllegalArgumentException (非法参数异常);
IndexOutOfBoundsException (数组下标越界异常);
NullPointerException (空指针异常)。
24、Java集合框架
图示:
1.常见的集合有哪些?
答:Map接口和Collection接口是所有集合框架的父接口:
(1)Collection接口的子接口包括:Set接口和List接口
(2)Map接口的实现类主要有:HashMap、TreeMap、Hashtable、ConcurrentHashMap以及Properties等
(3)Set接口的实现类主要有:HashSet、TreeSet、LinkedHashSet等
(4)List接口的实现类主要有:ArrayList、LinkedList、Stack以及Vector等
25、Thread类的sleep()方法和对象的wait()方法都可以让线程暂停执行,它们有什么区别?
sleep():是线程类Thread的静态方法,调用该方法使线程暂停执行指定的时间,将CPU让给其他线程,并不释放所持有的对象锁,休眠时间结束后线程回到就绪状态。
wait():Object类的方法,调用wait()方法,线程释放所持有的对象锁,进入等待池中,只有调用notify()方法或notifyAll()方法,才能唤醒等待池中的线程进入等锁池,若线程获得对象的锁,则线程重新进入就绪状态。
26、当一个线程进入一个对象的synchronized方法A之后,其它线程是否可进入此对象的synchronized方法B?
不能,因为synchronized修饰符要求执行方法时要获得对象的锁,如果已经进入A方法说明对象锁已经被取走,那么试图进入B方法的线程就只能在等锁池中等待对象的锁。
27、请说出与线程同步以及线程调度相关的方法
wait():使当前执行线程处于等待状态,并且释放所持有的对象的锁;
sleep():使当前执行线程睡眠指定的时间,不释放持有的对象锁,静态方法,调用此方法要处理InterruptedException异常;
notify():唤醒一个处于等待状态的线程,调用此方法并不能保证唤醒哪个线程,而是由JVM确定唤醒哪个线程,而且与优先级无关;
notityAll():唤醒所有处于等待状态的线程,只有获得锁的线程才能进入就绪状态;
28、synchronized和Lock区别
类别 | synchronized | Lock |
---|---|---|
存放层次 | Java关键字,JVM层面 | 是一个类,位于java.util包下 |
锁释放 | 1.执行完同步代码,自动释放锁 2.发生异常,JVM会让线程释放锁 | 手动在finally中释放,不然会发生死锁 |
锁状态 | 无法判断 | 可以判断 |
锁类型 | 可重入锁、不可中断、非公平锁 | 可重入锁、可判断、公平锁 |
性能 | 少量同步 | 大量同步 |
29、事务的ACID(四大特性)是指什么?
- 原子性(Atomic):事务中各项操作,要么全做要么全不做;
- 一致性(Consistent):事务结束后系统状态是一致的;
- 隔离性(Isolated):并发执行的事务彼此独立;
- 持久性(Durable):事务完成后所做的改动都会被持久化到数据库。
30、事务并发问题
**脏读:**事务A读取了事务B更新的数据,然后B回滚操作,那么A读取到的数据是脏数据(读取到了未提交数据);
**不可重复读:**事务A多次读取同一数据,事务B在事务A多次读取的过程中对数据作了更新并提交,导致事务A多次读取同一数据时结果不一致;
**幻读:**事务A重复读取数据,读取到了被事务B修改过的数据,像发生了幻觉一样,这就叫幻读;
**注意:**不可重复读和幻读很容易混淆,不可重复读侧重于修改,幻读侧重于新增或删除。
31、事务的隔离级别
读取未提交:不可避免幻读,脏读及不可重复读;
读取已提交:可避免脏读,不可避免幻读和不可重复读,;
可重复读:可避免脏读和不可重复读,不可避免幻读;
可串行化:可避免幻读,脏读及不可重复读。
32、成员变量和局部变量的区别?
- 从语法形式看:成员变量存在于类中,局部变量存在于方法中或作为方法的参数存在;成员变量可以被public、static等修饰符所修饰,局部变量不可以(因为访问修饰符用于区分访问范围,位于方法中的变量的作用域只限于方法体内,而static修饰的变量一般用作共享数据使用);
- 在内存中的存储位置看:若static修饰成员变量,则属于类变量(静态随着类的加载而加载),否则属于实例变量。对象那存在于堆内存,局部变量存在于栈内存;
- 在内存中的生命周期:成员变量随着实例对象而创建,局部变量随着方法结束而消亡;
- 默认初始值:成员变量可以没有初始值(final修饰的成员变量需要显示赋值),局部变量必须有初始值。
33、构造方法的作用
构造方法与类名相同,无返回值(不是返回值为void),用于完成对象的初始化。如果一个类没有定义构造方法,则系统会默认提供一个无参构造。
34、静态方法和实例方法(非静态方法)区别?
调用方式:使用“类名.方法名”或者“对象.方法名”调用,实例方法只能用“对象.方法名”调用;
访问形式:静态方法只能访问静态成员,因为静态方法随着类加载,实例方法随着对象加载,先有类才有对象。有可能出现对象还没创建,静态方法就去调用实例方法,这时就会把报错。
35、静态方法调用的三种方式
- 通过实例对象调用;
- 通过类名直接调用;
- 一个类中的静态方法可以直接调用。
36、super和this关键字的使用
super关键字:用来访问父类内容
- 在子类成员方法中,访问父类成员变量;
- 在子类成员方法中,调用父类成员方法;
- 在子类构造方法中,调用父类构造方法。
public class Zi extends Fu {
int num = 20;
public Zi(){
super();//访问父类构造方法
}
public void methodZi(){
System.out.println(super.num); //访问父类的员变量
}
public void method(){
super.method(); //访问父类成员方法
}
}
public class Fu {
int num = 10;
public void method(){
System.out.println("hello world!");
}
}
this关键字:用来访问本类内容
- 在本类成员方法中,访问本类成员变量;
- 在本类成员方法中,调用本类其他成员方法;
- 在本类构造方法中,调用本类其他构造方法。
注意:
在本类构造方法中使用this关键字调用本类其他构造方法,需放在构造方法第一行;
super和this不能同时调用构造方法。
public class Zi extends Fu{
int num = 20;
public Zi(){
//super();
this(134); //本类的无参构造,调用本类的有参构造
}
public Zi(int n ){
}
public Zi(int n, int m ){
}
public void showNum(){
int num = 10;
System.out.println(this.num); //访问本类成员变量
System.out.println(super.num); //访问父类成员变量
}
}
public class Fu {
int num =30;
}
37、静态代码块、非静态代码块和构造方法的执行顺序
public class Test {
public Test() {
System.out.println("构造方法");
}
{
System.out.println("非静态代码块");
}
static {
System.out.println("静态代码块");
}
public static void main(String[] args) {
new Test();
}
}
/**
输出结果:
静态代码块
非静态代码块
构造方法
*/
- 被static修饰的随着类加载,所以最先执行;
- 非静态代码块是给所有对象进行统一初始化;
- 而构造函数是给对应的对象初始化。
38、序列化与反序列化
- 为什么要序列化Java对象
- 数据在计算机上是以字节形式进行传输的,而不是以对象的形式进行传输,所以需要将对象进行序列化;
- 概念:
- 序列化:把Java对象转换为字节流的过程;
- 反序列化:把字节流恢复为Java对象的过程。
- 用途:
- 把对象永久地保存到硬盘上,比如保存至数据库等地方;
- 在网络上进行对象的传输。
39、命名规范
- 名称只能由字母、数字、下划线、$符号组成;
- 不能以数字开头;
- 名称不能使用Java中的关键字;
- 坚决不允许出现中文及拼音命名。
40、抽象类和抽象方法
抽象类:由abstract关键字修饰的类,不能被实例化,只能被继承;
抽象方法:由abstract关键字修饰的方法,没有方法体,不能被调用;
两者关系:含有抽象方法的类一定为抽象类,抽象类中可以有非抽象方法;
一旦一个类继承了抽象类,那这个类要么实现抽象类中的所有抽象方法,要么继续抽象下去;
41、如何遍历Map集合
package stringdemo;
import java.util.*;
public class StringDemo {
public static void main(String[] args) {
Map<String, String> map = new HashMap<>();
map.put("刘能", "42");
map.put("谢广坤", "46");
map.put("赵四", "47");
map.put("谢腾飞", "12");
//只获取值
Collection<String> values = map.values();
for (String value : values) {
System.out.println(value);
}
//通过keySet()方法获取key的集合,进而获取value
Set<String> keySet = map.keySet();
for (Object o : keySet) {
System.out.println(o + "---" + map.get(o));
}
//通过entrySet()方法获取此集合K-V映射关系的Set集合,进而遍历此集合得到键和值
Set<Map.Entry<String, String>> entries = map.entrySet();
for (Map.Entry<String, String> entry : entries) {
System.out.println(entry.getKey() + "---" + entry.getValue());
}
//通过迭代器遍历
Iterator<Map.Entry<String, String>> it = map.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<String, String> entry = it.next();
System.out.println(entry.getKey() + "---" + entry.getValue());
}
}
}
42、接口说明
接口中的方法默认为public abstract;变量默认为public static final。
43、sleep()、wait()和yield()方法的区别
sleep()和wait()的区别:
- sleep方法是Thread的静态方法,wait方法是Object类的普通方法;
- sleep方法不释放同步锁,wait方法释放同步锁;
- wait方法用于线程间通信,而sleep方法用于短暂的暂停线程;
- sleep()方法的休眠时间结束后自动进入就绪状态(而不是运行状态),wait()方法需要调用notify()或者notifyAll()方法才能进入就绪状态。
sleep()和yield()的区别:
- sleep()方法不考虑线程优先级;yield()方法只会给相同优先级或者更高优先级线程机会;
- 线程执行sleep()方法进入阻塞状态,执行yield()方法进入就绪状态。
44、instanceof运算符的作用
用来判断一个对象是不是一个类的实例、子类的实例及一个接口的实现类,如果是则返回true,否则返回false。
格式:引用类型变量 instanceof(类、抽象类或接口)
45、节点流和处理流
按照流是否直接与特定的地方(如磁盘、内存、设备等)相连,分为节点流和处理流两类。
节点流:可以从一个特定的地方(节点)读写数据,如FileReader;
处理流:是对一个已存在的流的连接和封装,通过所封装的流的功能调用实现数据读写,如BufferedReader。处理流的构造方法总是要带一个其他的流对象作参数。
46、类的初始化过程
父类静态-->子类静态-->父类非静态-->父类构造-->子类非静态-->子类构造
规律:父类优先先子类,静态优先非静态;其中静态包含静态代码块与静态方法,这个谁在前面,则先执行谁。
47、多线程相关方法
- sleep会使当前线程睡眠指定时间,不释放锁;
- yield会使当前线程重回到就绪状态,等待cpu的调度,不释放锁;
- wait会使当前线程回到线程池中等待,释放锁,当被其他线程使用notify,notifyAll唤醒时进入就绪状态;
- 当前线程调用某线程join方法时,会使当前线程等待某线程执行完毕再结束,底层调用了wait,释放锁。
48、Java标识符
Java标识符由**数字、字母、下划线(_)、美元符号($)**组成, 首位不能是数字,并且 Java关键字不能作为标识符 。
49、Integer的缓存范围
-128—127
50、abstract和static不能一起用?
被static修饰的属于类级别,是被共享的资源,而不是被子类继承的;
被abstract修饰的则必须被子类所实现;
两个关键字同时出现显然是矛盾的。