@x-power
2022-12-27T05:19:50.000000Z
字数 8319
阅读 749
面试
重写: 表示将子类中的方法与父类中的某个方法的名称和参数完全相同,这个时候子类重写了父类的该方法, 这个是多态性的一种表现.
子类覆盖父类的时候只能抛出比父类更少的异常, 因为子类用于解决父类存在的一些问题, 所以不能比父类有更多的问题. --- 越来越完美
子类方法的访问权限只能比父类的更大,不能更小, 如果父类的该方法使用私有修饰符修饰的话, 这样就不存在继承. --- 越来越开放.
参数 | 抽象类 | 接口 |
---|---|---|
默认的方法实现 | 可以有默认的方法实现 | 接口是抽象的,不存在方法的实现(JDK8 default) . |
实现 | 子类使用extend关键字来继承抽象类,如果子类不是抽象类的话,他需要提供抽象类中所有声明方法的实现. | 子类使用关键字来implates来实现接口, 他需要提供接口中所有声明的方法和实现. |
构造器 | 抽象类可以有构造器 | 接口不能有构造器 |
与正常的Java类的区别 | 除了不能实例化抽象类之外,它和普通的Java类没有区别(实例化抽象类, 其实是匿名类,需要将其中所有的抽象函数实现之后可以实现匿名类.) | 接口和类不是同一个东西. |
访问修饰符 | 抽象方法可以有public,protected,default这些访问修饰符. | 接口方法默认的修饰符是public,你不可以使用其他修饰符 |
main 方法 |
抽象方法可以有main 方法,并且我们可以运行它 |
接口没有main 方法,因此我们不能运行它 |
多继承 | 抽象方法可以继承一个类和实现多个接口 | 接口只能继承一个或者多个其他接口 |
速度 | 比接口的速度更快 | 接口式比较慢的,因为它需要时间去寻找在类中实现的方法 |
添加新方法 | 如果向抽象类中添加新的方法,你可以给他提供默认的实现,因此你不需要改变你现在的代码 | 如果在接口中添加方法,那么你必须改变实现该接口的类. |
package pers.ycy.base;
public class FinalClass {
public static void main(String[] args) {
Animal ag = new Dog();
new Dog();
new Dog().run();
Animal animal = new Animal() {
@Override
protected void sing() {
System.out.println("sing");
}
};
animal.sing();
}
}
/**
* 抽象类, 动物.
*/
abstract class Animal {
int age;
String name;
public static void main(String[] args) {
Animal sing_sing = new Animal() {
@Override
protected void sing() {
System.out.println("sing sing");
}
};
sing_sing.run();
}
// 每次创建 新对象的时候会调用一次, 运行优先级 高于构造函数 低于静态代码块.
{
System.out.println("构造代码块");
}
// 在第一次创建对象的时候会执行一次. 运行优先于 构造代码块, 通常用于初始化只调用一次的数据, 例如: 初始化程序的运行配置信息
static {
System.out.println("静态代码块");
}
Animal() {
System.out.println("抽象类的构造函数");
}
abstract protected void sing();
protected void run() {
{
int x = 10;
System.out.println(x);
System.out.println("用于限定变量的作用域.");
}
System.out.println("动物是可以跑得");
}
}
class Dog extends Animal {
@Override
protected void sing() {
System.out.println("汪汪");
}
}
abstract class AGou extends Dog{
}
public class FinalClass {
public static void main(String[] args) {
Dog dog = new Dog();
dog.eat();
}
}
/**
* 接口和类 是不同的, 抽象类中的 构造代码块, 静态代码块, 构造函数. 接口中是没有的.
*/
interface Animal{
default void eat(){
System.out.println("接口的默认方法, 如果使用该方法, 则该方法需要对所有实现该接口的类都有用 .");
}
}
class Dog implements Animal{
}
获取一个对象对应的反射类在,Java方法中有下面几种方法可以获得反射类.
getClass()
Class.forName()
类.class
getClassLoader()
在支持Session复制的Web服务器上,通过修改Web服务器的配置,可以实现将Session同步到其它Web服务器上,达到每个Web服务器上都保存一致的Session.
将用户的每次请求都通过某种方法强制分发到某一个Web服务器上,只要这个Web服务器上存储了相应的Session数据,就可以实现会话跟踪.
在单独的服务器或者服务器集群上使用缓存技术,如Redis存储Session数据,集中管理所有的Session.所有的Web服务器都从这个存储介质中去的对应的Session,实现Session共享.
这种方式每次发起请求的时候都需要将Session数据放到Cookie中传递给服务端.
DriverManager
类注册驱动数据库驱动程序.DriverManager.getConnection
方法,通过JDBC URL将用户名,密码,数据表等信息传入,以得到数据库链接.MVC是三个单词的首字母缩写, 他们是Model, View, Controller. 这个模式认为, 程序不论简单与复杂, 从结构上来看,都可以分为三层:
equals
和==
的区别两个对象用
equals
比较返回true
,那么两个对象的hashcode
方法必须返回相同的结果.
hashtable
中, 两个自认为相同的对象(equals
为true
), 在取值和存值的时候就会出现, 下表地址不同的情况. 这样就麻烦很大了.两个对象用
equals
比较返回false
,那么两个对象的hashcode
方法也不一定返回不同的值,但是最好返回不同的值,这样可以提高哈希表的性能.
根据第二点,重写
equals
的时候,必须重写hashcode
,以确保equals
方法相等的时候两个对象的hashcode
返回相同的值.
equals
和hashcode
应该保持一致性.set集合去重的时候, 判断是否相等用的是 哈希值和equals同时成立与否. 这样也可以看出应该让 equals
和hashcode
应该保持一致性.
equals
和==
的区别是: ==
用于比较原生类型,而equals
方法用于检查对象的相等性.==
和equals
用于比较对象,当两个引用地址相同,==
返回true
.而equals
可以返回true
或false
主要取决于重写的实现.最常见的一个例子,字符串的比较,不同情况==
和equals
返回不同的结果.list
和map
的区别.
Map
是键值对的方式进行存储的, 根据值去获取键,List
是根据下标进行存储的, 所以必须要有顺序,有下标才能顺利的进行存取. 但是Map
就不需要下标,且没有顺序. 只需计算Hash值即可
Map
接口有三个实现类:HashMap
,HashTable
,LinkedHashMap
.
List
接口有三个实现类:LinkList
,ArrayList
,Vector
.
LinkedList
: 底层基于链表实现, 链表内存是散乱的, 每一个内节点都存储着 上一个节点的地址和下一个节点的地址以及本身的值.
Map
相当于和Collection
一个级别的,Map
集合存储键值对,且要求保持键的唯一性.
Map
是基于哈希存储的所以要求保持键的唯一性.其中区别: List特点 元素放入有顺序,元素且可以重复. Map特点 元素按键值对存储,无放入顺序.
List
和Set
的区别
List
和Set
都是继承自Collection接口.
List
特点: 要么是链表要么是顺序数组,所以有顺序,元素可重复.
Set
特点: HashSet(底层是HashMap),LinkedHashSet.
ArrayList
和LinkList
的区别其实就是顺序数组和链表的区别
ArrayList
和Vector
的区别都是顺序数组, 但是
Vector
是线程安全的,ArrayList
是线程不安全的.
Vector
的数据增长是原来的一倍,ArrayList
是增加原来的50%.
Vector
这样可以减少因为扩容产生的时间消耗. ArrayList
.HashMap
和HashTable
HashMap几乎可以等价于HashTable,除了HashMap是非
synchronized的
,并且可以接受null
(HashMap
可以接受为null
的键值和值,而HashTable
则不可以).
HashMap
是非synchronize
,而HashTable
是synchronize
的.这意味着HashTable
是线程安全的,多个线程可以共享一个HashTable
,如果没有一个正确的同步的话,多个线程是不能共享HashMap
的, Java5提供了ConcurrentHashMap,它是HashMap的替代,比HashMap的扩展性更好.
Synchronization
关键字.java.util.concurrent.atomic
包中的原子类, 例如AtomicInteger
.java.util.concurrent.locks
包中的锁ConcurrentHashMap
volatile
关键字,保证变量可见性(直接从内存读,而不是从线程 cache
读)在JVM底层volatile 是采用“内存屏障”来实现的.
缓存一致性协议(MESI协议)它确保每个缓存中使用的共享变量的副本是一致的。其核心思想如下:当某个 CPU 在写数据时,如果发现操作的变量是共享变量,则会通知其他 CPU 告知该变量的缓存行是无效的,因此其他 CPU 在读取该变量时,发现其无效会重新从主存中加载数据
synchronized
与 lock
的区别synchronized
与 lock
的区别
synchronized(隐式锁)
: 在需要同步的对象中加入此控制, synchronized
方法上, 也可以加在特定代码块中, 括号中表示需要上锁的对象.lock(显示锁)
: 需要显示指定其实位置和终止位置. 一般使用ReentrantLock
类作为锁, 多个线程中必须要使用一个 ReentrantLock
类做为对象才能保证锁的生效。且在加锁和解锁处需要通过 lock()
和 unlock()
显示指出。所以一般会在 finally
块中写 unlock()
以防死锁。synchronized
和 lock
性能区别
synchronized
是托管给 JVM
执行的,而 lock
是 Java
写的控制锁的代码。在 JDK 1.5
中,synchronize 是性能低效的。因为这是一个重量级操作,需要调用操作接口,导致有可能加锁消耗的系统时间比加锁以外的操作还多。相比之下使用 Java
提供的 Lock
对象,性能更高一些。但是到了 JDK 1.6
,发生了变化。synchronize
在语义上很清晰,可以进行很多优化,有适应自旋,锁消除,锁粗化,轻量级锁,偏向锁等等。导致在 JDK 1.6
上 synchronize
的性能并不比 Lock
差。
public static void main(String[] args) {
ok:
for(int i=0;i<10;i++){
for(int j=0;j<10;j++){
if(j==5){
System.out.println("j==5");
break ok;
}
}
}
}
final 修饰一个关键字的时候,是栈内存不能变,和堆内存无关。
public static void main(String[] args) {
final StringBuilder a = new StringBuilder("1qwe");
a.append("asdasd");
}
String
被声明为final类型,因此不可以继承
String
使用final
修饰的原因如下
字符串是不可变的, 所以是多线程安全的, 同一个字符串可以被多个线程共享. 这样便不用因为线程安全问题而使用同步(牵扯到Volatile关键字). 字符串自己便是线程安全的.
数据库的用户名,密码都是以字符串的形式传入来获得数据库的链接, 或者在Socket编程中, 主机的主机名和端口都是以字符串的形式传入的, 因为字符串是不可变的, 所以值是不可变的, 否则黑客可以改变字符串指向的对象的值, 造成安全漏洞. (非安全专业, 但是这种东西 还是和纸糊的墙差的不).
字符串是不可变的, 所以他在创建的时候HashCode就被缓存了, 不需要重新计算, 这就使得字符串很适合作为Map中的键, 字符串的处理速度也要快过其他键的对象. 所以HashMap中的键一般都是字符串.
静态方法:静态方法在类加载的时候就已经存在了、他不依赖于任何实例。所以静态方法必须有实现,不能是抽象方法。并且方法中不能有super
和this
关键字,因为这两个关键字适合具体对象相关联的。
静态语句块:在类初始化的时候执行一次,一般用于初始化该类的一些信息。
静态内部类:非静态内部类依赖于外部类的实例,也就是说需要先创建外部类实例,才可以用这个实例去创建非静态内部类。而静态内部类就可以直接创建。
monitor lock
从而进入synchronized
函数或者代码块,但是其他线程已经占用了该monitor
,所以处于阻塞状态。要结束该状态进入状态2需要其他线程释放monitor lock
。阻塞和等待的区别:阻塞是被动的,它是在等待获取monitor lock
。而等待是主动的,通过调用Object.wait()
等方法进入。
Waiting
状态进入和退出的方法:
进入方法 | 退出方法 |
---|---|
没有设置Timeout 参数的Object.wait() 方法 |
Object.notify()/Object.notifyAll() |
没有设置Timeout 参数的Thread.join() 方法 |
被调用的线程执行完毕 |
LockSupport.park() 方法 |
LockSupport.unpark(Thread) |