1. final相关,抽象类可以用final修饰吗
使用 final 关键字声明类、变量和方法需要注意以下几点:
- final 用在变量的前面表示变量的值不可以改变,此时该变量可以被称为常量。
表示该变量一旦被初始化便不可改变,这里不可改变的意思对基本类型变量来说是其值不可变,而对对象引用类型变量来说其引用不可再变。其初始化可以在两个地方:一是其定义处,也就是说在 final 变量定义时直接给其赋值;二是在构造方法中。这两个地方只能选其一,要么在定义时给值,要么在构造方法中给值,不能同时既在定义时赋值,又在构造方法中赋予另外的值。
- final 用在方法的前面表示方法不可以被重写。
说明这种方法提供的功能已经满足当前要求,不需要进行扩展,并且也不允许任何从此类继承的类来重写这种方法,但是继承仍然可以继承这个方法,也就是说可以直接使用。在声明类中,一个 final 方法只被实现一次。
- final 用在类的前面表示该类不能有子类,即该类不可以被继承。
表示该类是无法被任何其他类继承的,意味着此类在一个继承树中是一个叶子类,并且此类的设计已被认为很完美而不需要进行修改或扩展。
【todo】
2. ==和equals
- 首先 object 类的 "==" 和 equals 没有区别,指的都是:A和B是同一个东西时返回true。比较的是引用类型的变量所指向的对象的地址;
- 很多类继承了object类之后,都会重写object类的 equals() 和 toString()方法。
==通常表明引用的是同一个东西(引用的地址相同),equals通常表明两个相同种类对象的内容相同(值相同)。 - 对于基本数据类型的变量,==直接比较其存储的 “值”是否相等;equals方法不能作用于基本数据类型的变量!
3. hashmap和hashtable区别
- HashMap几乎可以等价于Hashtable,除了HashMap是非synchronized的,并可以接受null(HashMap可以接受为null的键值(key)和值(value),而Hashtable则不行)。
- HashMap是非synchronized,而Hashtable是synchronized,这意味着Hashtable是线程安全的,多个线程可以共享一个Hashtable;而如果没有正确的同步的话,多个线程是不能共享HashMap的。Java 5提供了ConcurrentHashMap,它是HashTable的替代,比HashTable的扩展性更好。
- 另一个区别是HashMap的迭代器(Iterator)是fail-fast迭代器,而Hashtable的enumerator迭代器不是fail-fast的。所以当有其它线程改变了HashMap的结构(增加或者移除元素),将会抛出ConcurrentModificationException,但迭代器本身的remove()方法移除元素则不会抛出ConcurrentModificationException异常。但这并不是一个一定发生的行为,要看JVM。这条同样也是Enumeration和Iterator的区别。
- 由于Hashtable是线程安全的也是synchronized,所以在单线程环境下它比HashMap要慢。如果你不需要同步,只需要单一线程,那么使用HashMap性能要好过Hashtable。
- HashMap不能保证随着时间的推移Map中的元素次序是不变的。
4. 还知道哪些线程安全的容器
同步容器类:使用了synchronized
1.Vector
2.HashTable
并发容器:
3.ConcurrentHashMap:分段
4.CopyOnWriteArrayList:写时复制
5.CopyOnWriteArraySet:写时复制
Queue:
6.ConcurrentLinkedQueue:是使用非阻塞的方式实现的基于链接节点的无界的线程安全队列,性能非常好。
(java.util.concurrent.BlockingQueue 接口代表了线程安全的队列。)
7.ArrayBlockingQueue:基于数组的有界阻塞队列
8.LinkedBlockingQueue:基于链表的有界阻塞队列。
9.PriorityBlockingQueue:支持优先级的无界阻塞队列,即该阻塞队列中的元素可自动排序。默认情况下,元素采取自然升序排列
10.DelayQueue:一种延时获取元素的无界阻塞队列。
11.SynchronousQueue:不存储元素的阻塞队列。每个put操作必须等待一个take操作,否则不能继续添加元素。内部其实没有任何一个元素,容量是0
Deque:
(Deque接口定义了双向队列。双向队列允许在队列头和尾部进行入队出队操作。)
12.ArrayDeque:基于数组的双向非阻塞队列。
13.LinkedBlockingDeque:基于链表的双向阻塞队列。
Sorted容器:
14.ConcurrentSkipListMap:是TreeMap的线程安全版本
15.ConcurrentSkipListSet:是TreeSet的线程安全版本
5. String和StringBuffer,StringBuilder
- 运行速度:StringBuilder > StringBuffer > String
- 线程安全:StringBuilder是线程不安全的,而StringBuffer是线程安全的
如果一个StringBuffer对象在字符串缓冲区被多个线程使用时,StringBuffer中很多方法可以带有synchronized关键字,所以可以保证线程是安全的,但StringBuilder的方法则没有该关键字,所以不能保证线程安全,有可能会出现一些错误的操作。所以如果要进行的操作是多线程的,那么就要使用StringBuffer,但是在单线程的情况下,还是建议使用速度比较快的StringBuilder。 - 字符串连接运算符 (+) 在内部使用 StringBuilder 类。
6. 面向对象的特性,分别解释一下
面向对象的三个基本特征是:封装、继承、多态
- 封装
封装最好理解了。封装是面向对象的特征之一,是对象和类概念的主要特性。封装,也就是把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏。 - 继承
继承是指这样一种能力:它可以使用现有类的所有功能,并在无需重新编写原来的类的情况下对这些功能进行扩展。通过继承创建的新类称为“子类”或“派生类”,被继承的类称为“基类”、“父类”或“超类”。
要实现继承,可以通过“继承”(Inheritance)和“组合”(Composition)来实现。继承概念的实现方式有二类:实现继承与接口继承。实现继承是指直接使用基类的属性和方法而无需额外编码的能力;接口继承是指仅使用属性和方法的名称、但是子类必须提供实现的能力; - 多态性
多态性(polymorphisn)是允许你将父对象设置成为和一个或更多的他的子对象相等的技术,赋值之后,父对象就可以根据当前赋值给它的子对象的特性以不同的方式运作。简单的说,就是一句话:允许将子类类型的指针赋值给父类类型的指针。
实现多态,有两种方式,覆盖和重载。覆盖和重载的区别在于,覆盖在运行时决定,重载是在编译时决定。并且覆盖和重载的机制不同,例如在 Java 中,重载方法的签名必须不同于原先方法的,但对于覆盖签名必须相同。 - 有时还会有抽象这一特性:抽象是将一类对象的共同特征总结出来构造类的过程,包括数据抽象和行为抽象两方面,抽象只关注对象的哪些属性和行为,并不关注这此行为的细节是什么
7. 设计模式
8. 迭代器Iterator的原理
- 迭代器(Iterator)模式,又叫做游标(Cursor)模式。GOF给出的定义为:提供一种方法访问一个容器(container)对象中各个元素,而又不需暴露该对象的内部细节。
- util包中的迭代器实现是fast-fail迭代器,说白了就是一旦由修改就抛异常,在current包中迭代器是弱一致性迭代器,可以在遍历时增删。
- 弱一致性迭代器的原理太复杂了,还是算了。。。
【todo】
9. java static(原题是针对C++里的static修饰的类,但是java里面static只能修饰嵌套类)
- 静态变量:
- 运行时,Java 虚拟机只为静态变量分配一次内存,在加载类的过程中完成静态变量的内存分配。
- 在类的内部,可以在任何方法内直接访问静态变量。
- 在其他类中,可以通过类名访问该类中的静态变量。
- 静态方法:
- 静态方法不需要通过它所属的类的任何实例就可以被调用,因此在静态方法中不能使用 this 关键字,也不能直接访问所属类的实例变量和实例方法,但是可以直接访问所属类的静态变量和静态方法。另外,和 this 关键字一样,super 关键字也与类的特定实例相关,所以在静态方法中也不能使用 super 关键字。
- 在实例方法中可以直接访问所属类的静态变量、静态方法、实例变量和实例方法。
- 静态代码块:
- Java 类中的 static{ } 代码块,主要用于初始化类,为类的静态变量赋初始值,提升程序性能。
- 静态代码块类似于一个方法,但它不可以存在于任何方法体中。
- 静态代码块可以置于类中的任何地方,类中可以有多个静态初始化块。
- Java 虚拟机在加载类时执行静态代码块,所以很多时候会将一些只需要进行一次的初始化操作都放在 static 代码块中进行。
- 如果类中包含多个静态代码块,则 Java 虚拟机将按它们在类中出现的顺序依次执行它们,每个静态代码块只会被执行一次。
- 静态代码块与静态方法一样,不能直接访问类的实例变量和实例方法,而需要通过类的实例对象来访问。
- static 修饰的成员变量和方法,从属于类。
- 普通变量和方法从属于对象。
- 静态方法不能调用非静态成员,编译会报错。
10. 抽象类(原题是虚函数和纯虚函数,针对c++,java只有抽象类)
- 抽象方法没有方法体
- 抽象方法必须存在于抽象类中
- 子类重写父类时,必须重写父类所有的抽象方法
11. 反射机制
- Java的反射机制的核心是在程序运行时进行动态加载类并获取类的详细信息,从而操作类或对象的属性和方法。本质是JVM得到class对象后,再通过class对象进行反编译,从而获取对象的各种信息。
- 程序的JVM将使用类加载器子系统,类加载器首先检查这个类的class对象是否已经加载,如果尚未加载,默认的类加载器就会根据类名在本地磁盘查找.class文件,并将其载入,一旦某个类的class对象被载入内存,它就被用来创建这个类的所有对象。
12. 重写与重载
- 重写:重写是子类对父类的允许访问的方法的实现过程进行重新编写, 返回值和形参都不能改变。
重写规则:
- 参数列表与被重写方法的参数列表必须完全相同。
- 返回类型与被重写方法的返回类型可以不相同,但是必须是父类返回值的派生类(java5 及更早版本返回类型要一样,java7 及更高版本可以不同)。
- 访问权限不能比父类中被重写的方法的访问权限更低。例如:如果父类的一个方法被声明为 public,那么在子类中重写该方法就不能声明为 protected。
- 父类的成员方法只能被它的子类重写。
- 声明为 final 的方法不能被重写。
- 声明为 static 的方法不能被重写,但是能够被再次声明。
- 子类和父类在同一个包中,那么子类可以重写父类所有方法,除了声明为 private 和 final 的方法。
- 子类和父类不在同一个包中,那么子类只能够重写父类的声明为 public 和 protected 的非 final 方法。
- 重写的方法能够抛出任何非强制异常,无论被重写的方法是否抛出异常。但是,重写的方法不能抛出新的强制性异常,或者比被重写方法声明的更广泛的强制性异常,反之则可以。
- 构造方法不能被重写。
- 如果不能继承一个类,则不能重写该类的方法。
- 重载:在一个类里面,方法名字相同,而参数不同。返回类型可以相同也可以不同。
重载规则:
- 被重载的方法必须改变参数列表(参数个数或类型不一样);
- 被重载的方法可以改变返回类型;
- 被重载的方法可以改变访问修饰符;
- 被重载的方法可以声明新的或更广的检查异常;
- 方法能够在同一个类中或者在一个子类中被重载。
- 无法以返回值类型作为重载函数的区分标准。
13. Java实现单例模式
- 单例(Singleton)模式的定义:指一个类只有一个实例,且该类能自行创建这个实例的一种模式。
- 3 个特点:
- 单例类只有一个实例对象;
- 该单例对象必须由单例类自行创建;
- 单例类对外提供一个访问该单例的全局访问点。
- 实现:
- 懒汉式单例模式:类加载时没有生成单例,只有当第一次调用 getlnstance 方法时才去创建这个单例
public class LazySingleton { private static volatile LazySingleton instance = null; //保证 instance 在所有线程中同步 private LazySingleton() { } //private 避免类在外部被实例化 public static synchronized LazySingleton getInstance() { //getInstance 方法前加同步 if (instance == null) { instance = new LazySingleton(); } return instance; } }
- 饿汉式单例模式:类一旦加载就创建一个单例,保证在调用 getInstance 方法之前单例已经存在了。
public class HungrySingleton { private static final HungrySingleton instance = new HungrySingleton(); private HungrySingleton() { } public static HungrySingleton getInstance() { return instance; } }
14. 常用的java注解有哪些
【todo】
15. jvm区域
【todo】
16. gc算法
【todo】
17. 内存区域划分
【todo】
18. 线程池拒绝策略
【todo】
19. 当线程队列满了之后事项
【todo】
20. 多态
【todo】
21. 什么是装饰器
【todo】
22. 函数入参顺序
【todo】
23. hashmap往里put两个数据,如果它们的hashcode是一样的,那怎么区分查找出他们的值来
【todo】
24. java如何处理异常,有哪些异常
【todo】
25. jvm垃圾检测机制
【todo】
26. 面向对象程序的内存分配方式
【todo】
27. Runnable和Callable的区别
【todo】
28. 线程的创建方式
【todo】
29. 抽象类和接口的区别
【todo】
30. Array和ArrayList
【todo】
31. 面向对象的五大原则
- 单一职责原则(Single-Resposibility Principle):一个类,最好只做一件事,只有一个引起它的变化。单一职责原则可以看做是低耦合、高内聚在面向对象原则上的引申,将职责定义为引起变化的原因,以提高内聚性来减少引起变化的原因。
- 开放封闭原则(Open-Closed principle):软件实体应该是可扩展的,而不可修改的。也就是,对扩展开放,对修改封闭的。
- Liskov替换原则(Liskov-Substituion Principle):子类必须能够替换其基类。这一思想体现为对继承机制的约束规范,只有子类能够替换基类时,才能保证系统在运行期内识别子类,这是保证继承复用的基础。
- 依赖倒置原则(Dependecy-Inversion Principle):依赖于抽象。具体而言就是高层模块不依赖于底层模块,二者都同依赖于抽象;抽象不依赖于具体,具体依赖于抽象。
- 接口隔离原则(Interface-Segregation Principle):使用多个小的专门的接口,而不要使用一个大的总接口