Java 面试分享《一》

一、map 集合按 value 排序

  • 1.Comparator:在 compare 方法里面实现自己的排序逻辑
  • 2.Map.Entry:Map 的数据结构是一个个的 Entry,利用 Map 提供的 api 获取到整个 Map 的 Entry 的集合
  • 3.Collections.sort(List, Comparator):有了 Comparator,将 Map.Entry 放进 List,利用 Collections 的 sort 方法对 List 里面的元素进行排序
  • 4.附代码:
private static class ValueComparator implements Comparator {
	@Override
	public int compare(Entry entry1, Entry entry2) {
	    return entry2.getValue() - entry1.getValue();
	}
}
public static void main(String[] args) {
	Map map = new HashMap();
	map.put("a", 2);
	map.put("b", 4);
	map.put("c", 1);
	map.put("d", 5);
	map.put("e", 3);
	
	List<> list = new ArrayList<>();
	list.addAll(map.entrySet());
	Collections.sort(list, new ValueComparator());
	
	for (Map.Entry entry : list) {
	    System.out.println(entry.getValue());
	}
}

二、类的实例化顺序

  • 1.父类静态成员和静态初始化块,按在代码中出现的顺序依次执行
  • 2.子类静态成员和静态初始化块,按在代码中出现的顺序依次执行
  • 3.父类实例成员和实例初始化块,按在代码中出现的顺序依次执行
  • 4.父类构造方法
  • 5.子类实例成员和实例初始化块,按在代码中出现的顺序依次执行
  • 6.子类构造方法
  • 7.总结:先静态、先父后子。 先静态:父静态 > 子静态 。优先级:父类 > 子类,静态代码块 > 非静态代码块 > 构造函数。
  • 8.实例代码:
package com.company.demo1;
 
public class ClassInit {
    public static void main(String[] args) {
        System.out.println("1");
        new B();
    }
}
 
class A {
    static {
        System.out.println("A的static代码块...");
    }
 
    public String s1 = prtString("A的成员变量...");
    public static String s2 = prtString("A的static变量...");
 
    protected A() {
        System.out.println("A的构造函数...");
    }
 
    {
        System.out.println("A的代码块");
    }
 
    public static String prtString(String str) {
        System.out.println(str);
        return null;
    }
}
 
class B extends A {
    public String ss1 = prtString("B的成员变量...");
    public static String ss2 = prtString("B的static变量...");
 
    public B() {
        System.out.println("B的构造函数...");
    }
 
    private static A a = new A();
 
    static {
        System.out.println("B的static代码块...");
    }
 
    {
        System.out.println("B的代码块...");
    }
}
  • 9.返回结果如下:
    A的static代码块…
    A的static变量…
    B的static变量…
    A的成员变量…
    A的代码块
    A的构造函数…
    B的static代码块…
    A的成员变量…
    A的代码块
    A的构造函数…
    B的成员变量…
    B的代码块…
    B的构造函数…

三、描述一下类加载器

  • 1.什么是类加载器:
1.1 虚拟机设计团队把类加载阶段中的“通过一个类的全限定名来获取描述此来的二进制字节流”这个动作放到了 Java 虚拟机
外部去实现,一边让应用程序自己决定如何去获取所需要的类。实现这个动作的代码模块称为“类加载器”。
1.2 类加载器虽然只是用于实现类的加载动作,但是它在 java 程序中起到的作用却远远不限与类加载阶段。对于任意一个类
都需要由加载它的类加载器和这个类本身一同确立其在 Java 虚拟机中的唯一性,每一个类加载器,都拥有一个独立的类名称
空间。
  • 2.类加载器的分类:
2.1 启动类加载器(Bootstrap ClassLoader):负责加载 JAVA_HOME\lib 目录中并且能被虚拟机识别的类库加载到 JVM
内存中,如果名称不符合的类库即使在 lib 目录中也不会被加载。该类加载器无法被 java 程序直接引用。
2.2 扩展类加载器(Extension ClassLoader):该加载器主要负责加载 JAVA_HOME\lib\ext 目录中的类库,开发者可以使
用扩展加载器。
2.3 应用程序类加载器(Application ClassLoader):该列加载器也称为系统加载器,它负责加载用户类路径(Classpath)
上所指定的类库,开发者可以直接使用该类加载器,如果应用程序中没有自定义过自己的类加载器,一般情况下这个就是程序
中默认的类加载器。
2.4 自定义类加载器:要实现自定义类加载器,只需要继承 java.lang.ClassLoader 类,并且重写 findClass() 方法即
可。

四、动态代理的方式和优点

动态代理:是使用反射和字节码的技术,在运行期创建指定接口或类的子类,以及其实例对象的技术,通过这个技术可以无侵入的为代码进行增强

  • 1.JDK 动态代理:jdk 动态代理是由 java 内部的反射机制来实现的,而反射机制在生成类的过程中比较高效。
  • 2.CGLIB动态代理:cglib 动态代理是通过继承来实现的,底层则是借助asm(Java 字节码操控框架)来实现的(采用字节码的方式,给A类创建一个子类B,子类B使用方法拦截的技术拦截所有父类的方法调用),而asm在生成类之后的相关执行过程中比较高效(可以通过将asm生成的类进行缓存,这样解决asm生成类过程低效问题)。
  • 3.总结:
    jdk 动态代理有一定的局限性,只能基于接口进行代理(因为它已经继承了proxy 了,java 不支持多继承)。
    cglib 这种第三方类库实现的动态代理应用更加广泛,且在效率上更有优势。但cglib 通过继承的方式进行代理的,无论目标对象有没有实现接口都可以代理,但是无法处理 final 的情况(final 修饰的方法不能被覆写)。

五、通过反射创建类实例的三种方式

  • 1.方式一:任何数据类型(包括基本数据类型)都具备着一个静态的属性 class,通过它可直接获取到该类型对应的 Class 对象。这种方式要使用具体的类,然后调用类中的静态属性 class 完成,无需调用方法,性能更好。
  • 2.方式二:通过对象的 getClass 方法进行获取。这种方式需要具体的类和该类的对象,以及调用 getClass 方法。
  • 3.方式三:通过 Class.forName() 方法获取。这种方式仅需使用类名,就可以获取该类的 Class 对象,更有利于扩展。
  • 4.代码实例:
public class ClassDemo {
	public static void main(String[] args) {
	    
	    //Foo的实例对象如何表示
	    Foo foo1 = new Foo();//foo1就表示出来了
	    //Foo这个类,也是一个实例对象,Class类的实例对象,如何表示呢、
	    //任何一个类都是Class的实例对象,创建实例对象有三个方式
	    //第一种方式--》实际在告诉我们任何一个类都有一个隐含的静态成员变量class
	    Class class1 = Foo.class;
	    
	    //第二种方式  已经知道该类的对象通过getClass方法
	    Class class2 = foo1.getClass();
	    
	    /*
	     * 官网class1 ,class2表示了Foo类的类类型(class type)
	     * 万事万物 都是对象
	     * 类也是对象,是Class类的实例对象
	     * 这个对象我们称为该类的类类型
	     */
	    //不管class1 or class2 都代表了Foo类的类类型,一个类只可能是Class,类的一个实例对象
	    System.out.println(class1==class2);//true
	    
	    //第三种方式
	    Class class3 = null;
	    try {
	        class3 = Class.forName("com.imooc.reflect.Foo");
	    } catch (ClassNotFoundException e) {
	        e.printStackTrace();
	    }
	    //
	    System.out.println(class2==class3);//true
	    
	    //我们完全尅通过类的类类型创建该类的对象实例--》通过class1  or class2 or class3
	    //创建Foo类的实例对象
	    try {
	        //需要有无参数的构造方法
	        Foo foo = (Foo) class1.newInstance();//需要强转
	        foo.print();
	    } catch (Exception e) {
	        e.printStackTrace();
	    }
	}
}

class Foo{
    public void print(){
        System.out.println("foo");
    }
}

六、JavaBean 的生命周期

  • 1.实例化 bean
  • 2.设置 javaBean 的属性值
  • 3.若该 bean 实现了 BeanNameAware 接口,则调用该接口的 setBeanName() 方法
  • 4.若该 bean 实现了 BeanFactoryAware 接口,则调用该接口的 setBeanFactory() 方法
  • 5.若 sping 为所有 javaBean 配置了后处理器,即实现了 BeanPostPorcessor 接口的 java 类,并在配置文件中注册为 bean,调用 BeanPostProcessor 接口的 postProcessBeforeInitialization() 方法
  • 6.若 bean 实现了 InitializingBean 接口,则调用该接口的 afterPropertiesSet() 方法
  • 7.调用 bean 中自己定制的初始化方法:配置文件中配置 init-method, 7和8的初始化方法是平级的,可共存,效果一样,一般选其一即可
  • 8.调用 BeanPostPorcessor 接口的 postProcessAfterInitialization() 方法,容器销毁后,调用 bean 中定制的销毁方法
  • 9.若该 bean 实现了 DisposableBean 接口,调用其 destroy() 方法
  • 10.配置文件中指定自定义的销毁方法:destroy-method , 9和10是两种不同的销毁方式,是平级的,可共存,效果一样,一般选其一即可
  • 11.附 JavaBean 生命周期图:
    在这里插入图片描述

七、Spring 和 SpringBoot 的区别

  • 1.Spring 框架为开发 Java 应用程序提供了全面的基础架构支持。它包含一些很好的功能,如依赖注入和开箱即用的模块,如:Spring JDBC 、Spring MVC 、Spring Security、 Spring AOP 、Spring ORM 、Spring Test,些模块缩短应用程序的开发时间,提高了应用开发的效率。例如,在 Java Web 开发的早期阶段,我们需要编写大量的代码来将记录插入到数据源中。但是通过使用 Spring JDBC 模块的 JDBCTemplate,我们可以将这操作简化为只需配置几行代码。
  • 2.SpringBoot 基本上是 Spring 框架的扩展,它消除了设置 Spring 应用程序所需的 XML 配置,为更快,更高效的开发生态系统铺平了道路。以下是 SpringBoot 中的特点:
2.1 创建独立的 spring 应用。
2.2 嵌入 Tomcat,Jetty Undertow 而且不需要部署它们。
2.3 提供的 “starters” poms 来简化 Maven 配置。
2.4 尽可能自动配置 spring 应用。
2.5 提供生产指标,健壮检查和外部化配置。
2.6 绝对没有代码生成和 XML 配置要求。

八、Http 和 Https 的区别

  • 1.Http:超文本传输协议 HTTP 协议被用于在 Web 浏览器和网站服务器之间传递信息,HTTP 协议以明文方式发送内容,不提供任何方式的数据加密,如果攻击者截取了 Web 浏览器和网站服务器之间的传输报文,就可以直接读懂其中的信息,因此,HTTP 协议不适合传输一些敏感信息,比如:信用卡号、密码等支付信息。
  • 2.Https:HTTPS 协议是由 SSL/TLS+HTTP 协议构建的可进行加密传输、身份认证的网络协议,要比 HTTP 协议安全。HTTPS 协议的主要作用可以分为两种:一种是建立一个信息安全通道,来保证数据传输的安全;另一种就是确认网站的真实性。
  • 3.两者的联系:为了解决 HTTP 协议不适合传输一些敏感信息的缺陷,需要使用另一种协议:安全套接字层超文本传输协议 HTTPS,为了数据传输的安全,HTTPS 在 HTTP 的基础上加入了 SSL/TLS 协议,SSL/TLS 依靠证书来验证服务器的身份,并为浏览器和服务器之间的通信加密。
  • 4.两者的区别:
4.1 HTTP 的 ur l以 http:// 开头;而 HTTPS 的 url 以 https:// 开头。
4.2 HTTP 无需证书;HTTPS 协议需要到 CA 机构申请 SSL 证书,需要一定费用。
4.3 HTTP 无需加密,信息是明文传输;HTTPS 则是具有安全性的 SSL 加密传输协议。
4.4 HTTP 标准端口是80 ,而 HTTPS 的标准端口是443。
4.5 在 OSI 网络模型中,HTTP 工作于应用层,而 HTTPS 工作在传输层。
4.6 HTTP 的连接很简单,是无状态的;HTTPS 协议是由 SSL+HTTP 协议构建的可进行加密传输、身份认证的网络协议,比
HTTP 协议安全。

九、TCP/IP 的三次握手和四次挥手

  • 1.三次握手建立连接:
1.1 第一次握手:建立连接时,客户端A发送 SYN 包(SYN=j)到服务器 B,并进入 SYN_SEND 状态,等待服务器 B 确认。
1.2 第二次握手:服务器 B 收到 SYN 包,必须发生一个 ACK 包,来确认客户 A 的 SYN(ACK=j+1),同时自己也发送一
个 SYN 包(SYN=k),即 SYN+ACK 包,此时服务器 B 进入 SYN_RECV 状态。
1.3 第三次握手:客户端 A 收到服务器 B 的 SYN+ACK 包,向服务器 B 发送确认 ACK(ACK=k+1),此包发送完毕,客户
端 A 和服务器 B 进入 ESTABLISHED 状态,完成三次握手(注意,主动打开方的最后一个 ACK 包中可能会携带了它要发送
给服务端的数据)。
  • 2.三次握手总结:三次握手,其实就是主动打开方,发送 SYN,表示要建立连接,然后被动打开方对此进行确认,表示可以,然后主动方收到确认之后,对确认进行确认。
  • 3.四次挥手断开连接:
第一阶段:主要是主动闭方方发生FIN,被动方对它进行确认。
3.1 第一次挥手:主动关闭方,客户端发送完数据之后,向服务器发送一个 FIN(M) 数据包,进入 FIN_WAIT1 状态;被动关
闭方服务器收到 FIN(M) 后,进入 CLOSE_WAIT 状态。
3.2 第二次挥手:服务端发生 FIN(M) 的确认包 ACK(M+1),关闭服务器读通道,进入 LAST_ACK 状态;客户端收到 
ACK(M+1)后,关闭客户端写通道, 进入 FIN_WATI2 状态;此时客户端仍能通过读通道读取服务器的数据,服务器仍能通过
写通道写数据。
第二阶段:主要是被动关闭方发生FIN,主动方对它进行确认。
3.3 第三次挥手:服务器发送完数据,向客户机发送一个 FIN(N) 数据包,状态没有变还是 LAST_ACK;客户端收到 FIN(N)
后,进入 TIME_WAIT 状态。
3.4 第四次挥手:客户端返回对 FIN(N) 的确认段 ACK(N+1),关闭客户机读通道(还是 TIME_WAIT 状态);服务器收到
ACK(N+1) 后,关闭服务器写通道,进入 CLOSED 状态。
  • 4.四次挥手总结:四次挥手,其本质就是主动关闭方数据发生完成之后发生 FIN,表示我方数据发生完,要断开连接,被动方对此进行确认;然后被动关闭方在数据发生完成之后发生 FIN,表示我方数据发生完成,要断开连接,主动方对此进行确认。

十、什么是 ACID

ACID 是数据库事务的四个特性:原子性(Atomicity,或称不可分割性),一致性(Consistency),隔离性(Isolation),持久性(Durability)

  • 1.原子性:原子性是指一个事务是一个不可分割的工作单位,其中的操作要么都做,要么都不做;如果事务中一个 sql 语句执行失败,则已执行的语句也必须回滚,数据库退回到事务前的状态。
  • 2.一致性:一致性是指事务执行结束后,数据库的完整性约束没有被破坏,事务执行的前后都是合法的数据状态。数据库的完整性约束包括但不限于:实体完整性(如行的主键存在且唯一)、列完整性(如字段的类型、大小、长度要符合要求)、外键约束、用户自定义完整性(如转账前后,两个账户余额的和应该不变)。
  • 3.隔离性:与原子性、持久性侧重于研究事务本身不同,隔离性研究的是不同事务之间的相互影响。隔离性是指事务内部的操作与其他事务是隔离的,并发执行的各个事务之间不能互相干扰。严格的隔离性,对应了事务隔离级别中的 Serializable (可串行化),但实际应用中出于性能方面的考虑很少会使用可串行化。
  • 4.持久性:持久性是指事务一旦提交,它对数据库的改变就应该是永久性的。接下来的其他操作或故障不应该对其有任何影响。

十一、Synchronized 和 Concurrent.locks.Lock 的区别

  • 1.Lock 能完成几乎所有 synchronized 的功能,并有一些后者不具备的功能,如锁投票、定时锁等候、可中断锁等候等。
  • 2.synchronized 是 Java 语言层面的,是内置的关键字;Lock 则是 JDK 5中出现的一个包。在使用时,synchronized 同步的代码块可以由 JVM 自动释放;Lock 需要程序员在 finally 块中手工释放,如果不释放,可能会引起难以预料的后果(在多线程环境中)。
  • 3.附图片:
    在这里插入图片描述

十二、引起线程死锁的原因,如何解除死锁

  • 1.产生死锁的原因:多个线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放,而该资源又被其他线程锁定,从而导致每一个线程都得等其它线程释放其锁定的资源,造成了所有线程都无法正常结束。即:(1) 竞争系统资源,(2) 进程的推进顺序不当
  • 2.产生死锁的四个必要条件:
2.1 互斥使用,即当资源被一个线程使用(占有)时,别的线程不能使用。
2.2 不可抢占,资源请求者不能强制从资源占有者手中夺取资源,资源只能由资源占有者主动释放。
2.3 请求和保持,即当资源请求者在请求其他的资源的同时保持对原有资源的占有。
2.4 循环等待,即存在一个等待队列:P1 占有 P2 的资源,P2 占有 P3 的资源,P3 占有 P1 的资源。这样就形成了一个等
待环路。
  • 3.解除死锁:
3.1 加锁顺序(线程按照一定的顺序加锁)
3.2 加锁时限(线程尝试获取锁的时候加上一定的时限,超过时限则放弃对该锁的请求,并释放自己占有的锁)
3.3 死锁检测
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值