[关闭]
@x-power 2022-12-27T05:19:50.000000Z 字数 8319 阅读 749

Java基础

面试


1. 重载和重写


2. 抽象类和接口的区别

参数 抽象类 接口
默认的方法实现 可以有默认的方法实现 接口是抽象的,不存在方法的实现(JDK8 default).
实现 子类使用extend关键字来继承抽象类,如果子类不是抽象类的话,他需要提供抽象类中所有声明方法的实现. 子类使用关键字来implates来实现接口, 他需要提供接口中所有声明的方法和实现.
构造器 抽象类可以有构造器 接口不能有构造器
与正常的Java类的区别 除了不能实例化抽象类之外,它和普通的Java类没有区别(实例化抽象类, 其实是匿名类,需要将其中所有的抽象函数实现之后可以实现匿名类.) 接口和类不是同一个东西.
访问修饰符 抽象方法可以有public,protected,default这些访问修饰符. 接口方法默认的修饰符是public,你不可以使用其他修饰符
main方法 抽象方法可以有main方法,并且我们可以运行它 接口没有main方法,因此我们不能运行它
多继承 抽象方法可以继承一个类和实现多个接口 接口只能继承一个或者多个其他接口
速度 比接口的速度更快 接口式比较慢的,因为它需要时间去寻找在类中实现的方法
添加新方法 如果向抽象类中添加新的方法,你可以给他提供默认的实现,因此你不需要改变你现在的代码 如果在接口中添加方法,那么你必须改变实现该接口的类.
  1. package pers.ycy.base;
  2. public class FinalClass {
  3. public static void main(String[] args) {
  4. Animal ag = new Dog();
  5. new Dog();
  6. new Dog().run();
  7. Animal animal = new Animal() {
  8. @Override
  9. protected void sing() {
  10. System.out.println("sing");
  11. }
  12. };
  13. animal.sing();
  14. }
  15. }
  16. /**
  17. * 抽象类, 动物.
  18. */
  19. abstract class Animal {
  20. int age;
  21. String name;
  22. public static void main(String[] args) {
  23. Animal sing_sing = new Animal() {
  24. @Override
  25. protected void sing() {
  26. System.out.println("sing sing");
  27. }
  28. };
  29. sing_sing.run();
  30. }
  31. // 每次创建 新对象的时候会调用一次, 运行优先级 高于构造函数 低于静态代码块.
  32. {
  33. System.out.println("构造代码块");
  34. }
  35. // 在第一次创建对象的时候会执行一次. 运行优先于 构造代码块, 通常用于初始化只调用一次的数据, 例如: 初始化程序的运行配置信息
  36. static {
  37. System.out.println("静态代码块");
  38. }
  39. Animal() {
  40. System.out.println("抽象类的构造函数");
  41. }
  42. abstract protected void sing();
  43. protected void run() {
  44. {
  45. int x = 10;
  46. System.out.println(x);
  47. System.out.println("用于限定变量的作用域.");
  48. }
  49. System.out.println("动物是可以跑得");
  50. }
  51. }
  52. class Dog extends Animal {
  53. @Override
  54. protected void sing() {
  55. System.out.println("汪汪");
  56. }
  57. }
  58. abstract class AGou extends Dog{
  59. }
  1. public class FinalClass {
  2. public static void main(String[] args) {
  3. Dog dog = new Dog();
  4. dog.eat();
  5. }
  6. }
  7. /**
  8. * 接口和类 是不同的, 抽象类中的 构造代码块, 静态代码块, 构造函数. 接口中是没有的.
  9. */
  10. interface Animal{
  11. default void eat(){
  12. System.out.println("接口的默认方法, 如果使用该方法, 则该方法需要对所有实现该接口的类都有用 .");
  13. }
  14. }
  15. class Dog implements Animal{
  16. }

3. 说说反射的用途和实现

反射的用途
反射的实现

4. Session和Cookie的区别

  1. Cookie是存放在客户端上的,Session存放在服务器上.
  2. Cookie不是很安全, 别人可以分析存放在本地的Cookie并进行Cookie欺骗,考虑到安全应当使用Session.
  3. Session会在一定时间内保存在服务器上, 当访问增多的时候会比较占用服务器的性能, 考虑到减轻服务器的压力应当使用Cookie.
  4. 单个Cookie保存的数据不应当超过4k, 很多浏览器限定一个站点最多保存20个Cookie.

5. GET请求和POST方式的区别

  1. 根据Http规范,GET用于信息获取, 而且应该是安全的和幂等性[1]
  2. 根据HTTP规范,POST请求可以改变服务器上的资源.
  3. 首先是"GET方式提交的数据最多只能是1024字节",因为GET是通过URL提交数据,那么GET可提交的数据量就和URK长度有直接的关系了.而实际上,URL参数不存在上限,HTTP协议规范没有对URL长度进行限制,这个限制是特定的浏览器以及服务器对它的限制,IE对URL长度的限制是2083(2k+35).对于其他浏览器. 一般没有长度限制
  4. POST没有大小限制,HTTP协议规范也没有进行大小限制.

6. Session分布式处理


6.1 Session复制

在支持Session复制的Web服务器上,通过修改Web服务器的配置,可以实现将Session同步到其它Web服务器上,达到每个Web服务器上都保存一致的Session.

  1. 优点: 代码上不需要做支持和修改.
  2. 缺点: 需要依赖支持Session复制的Web服务器, 一旦更换成不支持的Web服务器就不能使用了, 并且在用户量巨大的情况下可能会造成Session大爆炸.
  3. 适用场景: 只适用于Web服务器比较少并且Session数据量比较小的情况.
  4. 可用方案: 开源方案 tomcat-redis-session-manager, 暂不支持Tomcat8.

6.2 Session 粘滞

将用户的每次请求都通过某种方法强制分发到某一个Web服务器上,只要这个Web服务器上存储了相应的Session数据,就可以实现会话跟踪.

  1. 优点: 使用简单,没有额外开销.
  2. 缺点: 一旦某个Web服务器重启或者宕机,相对应的Session数据也会丢失,而且需要依赖负载均衡机制.
  3. 适用场景: 对稳定性要求不是很高的业务情景.

6.3 Session集中管理

在单独的服务器或者服务器集群上使用缓存技术,如Redis存储Session数据,集中管理所有的Session.所有的Web服务器都从这个存储介质中去的对应的Session,实现Session共享.

  1. 优点: 可靠性高,减少Web服务器的资源开销.
  2. 缺点: 实现上有些复杂,配置较多.
  3. 适用场景: Web服务器较多,要求高可用性的情况.
  4. 可用方案: 开源方案Spring Session,也可以自己实现,主要是重写HttpServletRequestWrapper中的getSession方法.

6.4 基于Cookie管理

这种方式每次发起请求的时候都需要将Session数据放到Cookie中传递给服务端.

  1. 优点: 不需要依赖额外外部存储,不需要额外配置.
  2. 缺点: 不安全,已被带去或篡改;Cookie的长度和数量有限制,需要消耗更多的网络带宽.
  3. 使用场景:数据不重要,不敏感且数据量较小的情况.

7. JDBC的流程


8. MVC设计思想

MVC是三个单词的首字母缩写, 他们是Model, View, Controller. 这个模式认为, 程序不论简单与复杂, 从结构上来看,都可以分为三层:

  1. 最顶层(View): 直接面向用户的视图层. 它是提供给用户的操作界面, 是程序的外壳.
  2. 最底层(Model): 核心的用户层, 也就是程序需要操作的数据或信息.
  3. 中层(Controller): 根据用户从"视图层"输入的指令,选取"数据层"中的数据,然后对其进行相应的操作,产生最终的结果.

9. equals==的区别

两个对象用equals比较返回true,那么两个对象的hashcode方法必须返回相同的结果.

两个对象用equals比较返回false,那么两个对象的hashcode方法也不一定返回不同的值,但是最好返回不同的值,这样可以提高哈希表的性能.

根据第二点,重写equals的时候,必须重写hashcode,以确保equals方法相等的时候两个对象的hashcode返回相同的值.


10. listmap的区别.

Map是键值对的方式进行存储的, 根据值去获取键, List是根据下标进行存储的, 所以必须要有顺序,有下标才能顺利的进行存取. 但是Map就不需要下标,且没有顺序. 只需计算Hash值即可

Map接口有三个实现类: HashMap,HashTable,LinkedHashMap.

List接口有三个实现类: LinkList,ArrayList,Vector.

LinkedList: 底层基于链表实现, 链表内存是散乱的, 每一个内节点都存储着 上一个节点的地址和下一个节点的地址以及本身的值.

Map相当于和Collection一个级别的, Map集合存储键值对,且要求保持键的唯一性.

Map是基于哈希存储的所以要求保持键的唯一性.

其中区别: List特点 元素放入有顺序,元素且可以重复. Map特点 元素按键值对存储,无放入顺序.


11. ListSet的区别

ListSet都是继承自Collection接口.
List特点: 要么是链表要么是顺序数组,所以有顺序,元素可重复.
Set特点: HashSet(底层是HashMap),LinkedHashSet.


12. ArrayListLinkList的区别

其实就是顺序数组和链表的区别


13. ArrayListVector的区别

都是顺序数组, 但是Vector是线程安全的,ArrayList是线程不安全的.
Vector的数据增长是原来的一倍, ArrayList是增加原来的50%.


14. HashMapHashTable

HashMap几乎可以等价于HashTable,除了HashMap是非synchronized的,并且可以接受null(HashMap可以接受为null的键值和值,而HashTable则不可以).

HashMap是非synchronize,而HashTablesynchronize的.这意味着HashTable是线程安全的,多个线程可以共享一个HashTable,如果没有一个正确的同步的话,多个线程是不能共享HashMap的, Java5提供了ConcurrentHashMap,它是HashMap的替代,比HashMap的扩展性更好.


15. 线程安全问题

在JVM底层volatile 是采用“内存屏障”来实现的.
缓存一致性协议(MESI协议)它确保每个缓存中使用的共享变量的副本是一致的。其核心思想如下:当某个 CPU 在写数据时,如果发现操作的变量是共享变量,则会通知其他 CPU 告知该变量的缓存行是无效的,因此其他 CPU 在读取该变量时,发现其无效会重新从主存中加载数据


16. synchronizedlock 的区别

17. Java跳出 一重以上循环的方式。

  1. public static void main(String[] args) {
  2. ok:
  3. for(int i=0;i<10;i++){
  4. for(int j=0;j<10;j++){
  5. if(j==5){
  6. System.out.println("j==5");
  7. break ok;
  8. }
  9. }
  10. }
  11. }

18. final / String

final 修饰一个关键字的时候,是栈内存不能变,和堆内存无关。

  1. public static void main(String[] args) {
  2. final StringBuilder a = new StringBuilder("1qwe");
  3. a.append("asdasd");
  4. }

String被声明为final类型,因此不可以继承

String使用final修饰的原因如下

1. 为了实现运行时常量池实现细粒度颗粒对象的复用.
2. 为了安全

字符串是不可变的, 所以是多线程安全的, 同一个字符串可以被多个线程共享. 这样便不用因为线程安全问题而使用同步(牵扯到Volatile关键字). 字符串自己便是线程安全的.

数据库的用户名,密码都是以字符串的形式传入来获得数据库的链接, 或者在Socket编程中, 主机的主机名和端口都是以字符串的形式传入的, 因为字符串是不可变的, 所以值是不可变的, 否则黑客可以改变字符串指向的对象的值, 造成安全漏洞. (非安全专业, 但是这种东西 还是和纸糊的墙差的不).

3. 为了实现String可以创建HashCode不可变性

字符串是不可变的, 所以他在创建的时候HashCode就被缓存了, 不需要重新计算, 这就使得字符串很适合作为Map中的键, 字符串的处理速度也要快过其他键的对象. 所以HashMap中的键一般都是字符串.


4. AbstractStringBuilder扩容大小是新字符串的2倍

19. static

静态方法:静态方法在类加载的时候就已经存在了、他不依赖于任何实例。所以静态方法必须有实现,不能是抽象方法。并且方法中不能有superthis关键字,因为这两个关键字适合具体对象相关联的。

静态语句块:在类初始化的时候执行一次,一般用于初始化该类的一些信息。

静态内部类:非静态内部类依赖于外部类的实例,也就是说需要先创建外部类实例,才可以用这个实例去创建非静态内部类。而静态内部类就可以直接创建。


20. 线程

  1. 新建:创建后但未启动。
  2. 可运行:正在Java虚拟机中运行,但是在操作系统层面,他可能处于运行状态也可能属于等待资源调度状态,资源调度完成之后进入运行状态,该状态的可运行是指可以被运行,具体有没有运行需要看底层操作系统的资源调度
  3. 阻塞态:去请求获取monitor lock从而进入synchronized函数或者代码块,但是其他线程已经占用了该monitor,所以处于阻塞状态。要结束该状态进入状态2需要其他线程释放monitor lock
  4. 无限期等待:线程休眠,需要其他线程显示的唤醒。
  5. 限期等待:无需等待其他线程显示的唤醒,在一定时间之后会被系统自动唤醒。

阻塞等待的区别:阻塞是被动的,它是在等待获取monitor lock。而等待是主动的,通过调用Object.wait()等方法进入。

Waiting状态进入和退出的方法:

进入方法 退出方法
没有设置Timeout参数的Object.wait()方法 Object.notify()/Object.notifyAll()
没有设置Timeout参数的Thread.join()方法 被调用的线程执行完毕
LockSupport.park()方法 LockSupport.unpark(Thread)

[1] 一次请求和多个请求对于同一个资源来说应该具有同样的结果(网络超时,电脑爆炸之类的不算数.).
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注