欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

面试必考:Java基础面试题(三)

程序员文章站 2022-06-10 10:49:23
...

19.请用至少四种写法写一个单例模式?

懒汉单例模式:没有用就不初始化,要用时,才初始化
懒汉式单例步骤:
1:静态属性,存储单例(所有类共享同一份数据)
2:私有的构造器,限制外部不能访问
3:静态方法,获取单例
4:返回单例名

public class Slacker {
/*
 * 懒汉单例模式:没有用就不初始化,要用时,才初始化
 */
	private static Slacker instance = null;//定义
	private void slacker() {
	}
	public static Slacker getInstance() {
		if (instance == null) {
        	instance = new Slacker();//初始化
    	}
    	return instance;
	}
}

饿汉式单例步骤:
1:相关属性初始化
2:开始时就创建实例
3:返回单例名

public class Hungry {    
/*    
饿汉单例模式:在还没有实例化的时候就初始化
 */
	private static final Hungry instance=new Hungry(); //初始化
	private void hungry() {    
	}
	public static Hungry getInstance() {return instance;		
	}
}

懒汉加锁:
在if前后加锁,第一个锁表示一次运行一个,第二个加锁表示没有实例就实例化

public class Lock_up {
	private static Lock_up instance = null;    
	private void Lock_up() {
	}
	private static Lock_up getinstance() {    
		synchronized (Lock_up.class) {// 加锁保证一次运行一个if (instance == null) {synchronized (Lock_up.class) {// 加锁保证instance为空时,创建一个实例
​					instance = new Lock_up();}}
		}    
		return instance;    
	}    
}

枚举单例模式:
枚举表达的是:
一个集的枚举是列出某些有穷序列集的所有成员,简单来说就是每个都列举出来。
枚举单例:
实现单例的核心在于private私有化类中的构造方法,在枚举中的构造方法必须是私有的,这就为枚举来实现单例奠定了基础。

/*
枚举单例:枚举的创建是通过JVM保证,不会出现并发,
构造器自动私有,外部本鞥访问,能能通过引用的方式。
例如:一周有七天,可以通过枚举的方式实现他。
  */
public enum Enum {
	INSTANCE;
	public void dosomething() { ​	
		System.out.println("dosomething");
	}
	public static void main(String[] args) {
		Enum.INSTANCE.dosomething();
	}
}

20.JAVA中的几种基本数据类型是什么,各自占用多少字节。1字节=8位

byte 1byte(字节)—short 2byte—int 4byte—long 8byte—float 4byte
Double 8byte----char 2byte—boolean 1bit

21.String类能被继承吗,为什么。

public final class String implements java.io.Serializable, Comparable, CharSequence {
// 省略… 
}

22.String,Stringbuffer,StringBuilder的区别。

String是Java中基础且重要的类,并且String也是Immutable类的典型实现,被声明为final class,除了hash这个属性其它属性都声明为final,因为它的不可变性,所以例如拼接字符串时候会产生很多无用的中间对象,如果频繁的进行这样的操作对性能有所影响。
StringBuffer就是为了解决大量拼接字符串时产生很多中间对象问题而提供的一个类,提供append和add方法,可以将字符串添加到已有序列的末尾或指定位置,它的本质是一个线程安全的可修改的字符序列,把所有修改数据的方法都加上了synchronized。但是保证了线程安全是需要性能的代价的。
在很多情况下我们的字符串拼接操作不需要线程安全,这时候StringBuilder登场了,StringBuilder是JDK1.5发布的,它和StringBuffer本质上没什么区别,就是去掉了保证线程安全的那部分,减少了开销。
StringBuffer 和 StringBuilder 二者都继承了 AbstractStringBuilder ,底层都是利用可修改的char数组(JDK 9 以后是 byte数组)。

23.ArrayList和LinkedList有什么区别。

ArrayList
底层是数组,查询快,增删慢,扩容((旧容量 3) / 2) + 1*
LinkedList
底层是链表,查询慢,增删快

24.讲讲类的实例化顺序,比如父类静态数据,构造函数,字段,子类静态数据,构造函数,字段,当new的时候,他们的执行顺序。

父类静态代变量、父类静态代码块、子类静态变量、子类静态代码块、父类非静态变量(父类实例成员变量)、父类构造函数、子类非静态变量(子类实例成员变量)、子类构造函数。

25.JAVA8的ConcurrentHashMap为什么放弃了分段锁,有什么问题吗,如果你来设计,你如何设计。

为什么放弃
加入多个分段锁浪费内存空间。
生产环境中, map 在放入时竞争同一个锁的概率非常小,分段锁反而会造成更新等操作的长时间等待。
为了提高 GC 的效率
现有的设计
put
首先通过 hash 找到对应链表过后, 查看是否是第一个object, 如果是, 直接用cas原则插入,无需加锁。

Node<K,V> f; int n, i, fh; K fk; V fv;
if (tab == null || (n = tab.length) == 0)
    tab = initTable(); // 这里在整个map第一次操作时,初始化hash桶, 也就是一个table
else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
//如果是第一个object, 则直接cas放入, 不用锁
    if (casTabAt(tab, i, null, new Node<K,V>(hash, key, value)))
        break;                   
}

然后, 如果不是链表第一个object, 则直接用链表第一个object加锁,这里加的锁是synchronized,虽然效率不如 ReentrantLock, 但节约了空间,这里会一直用第一个object为锁, 直到重新计算map大小, 比如扩容或者操作了第一个object为止。

synchronized (f) {// 这里的f即为第一个链表的object
    if (tabAt(tab, i) == f) {
        if (fh >= 0) {
            binCount = 1;
            for (Node<K,V> e = f;; ++binCount) {
                K ek;
                if (e.hash == hash &&
                    ((ek = e.key) == key ||
                     (ek != null && key.equals(ek)))) {
                    oldVal = e.val;
                    if (!onlyIfAbsent)
                        e.val = value;
                    break;
                }
                Node<K,V> pred = e;
                if ((e = e.next) == null) {
                    pred.next = new Node<K,V>(hash, key, value);
                    break;
                }
            }
        }
        else if (f instanceof TreeBin) { // 太长会用红黑树
            Node<K,V> p;
            binCount = 2;
            if ((p = ((TreeBin<K,V>)f).putTreeVal(hash, key,
                                           value)) != null) {
                oldVal = p.val;
                if (!onlyIfAbsent)
                    p.val = value;
            }
        }
        else if (f instanceof ReservationNode)
            throw new IllegalStateException("Recursive update");
    }
}

26.有没有有顺序的Map实现类,如果有,他们是怎么保证有序的。

Hashmap和Hashtable 都不是有序的。
TreeMap和LinkedHashmap都是有序的。(TreeMap默认是key升序,LinkedHashmap默认是数据插入顺序)
TreeMap是基于比较器Comparator来实现有序的。
LinkedHashmap是基于链表来实现数据插入有序的。

27.继承和聚合的区别在哪。

继承
指的是一个类继承另外的一个类的功能,并可以增加它自己的新功能的能力,继承是类与类或者接口与接口之间最常见的关系;在Java中此类关系通过关键字extends明确标识。
聚合
聚合体现的是整体与部分、拥有的关系,此时整体与部分之间是可分离的,他们可以具有各自的生命周期;比如计算机与CPU、公司与员工的关系等;