毕业季--Java基础面试题

Posted 编程指南针

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了毕业季--Java基础面试题相关的知识,希望对你有一定的参考价值。

面向对象三大特征

  • 封装
含义:
      封装就是把同一类事物的共性(包括属性和方法)归到同一类中,方便使用。
  实现:
      1.对成员变量进行私有化(使用private修饰);
      2.写成员变量对应的getter/setter方法 (用于属性的读写);
      3.在getter/setter方法中加入属性控制语句(用于判断属性值的合法性);
  优点:
      1.将变化隔离
      2.便于使用
      3.提高重用性
      4.提高安全性
  缺点:
      将变量等使用private修饰,或者封装进方法内,使其不能直接被访问,增加了访问步骤与难度
  • 继承
含义:
      在JAVA中, 被继承的类叫父类(parent class)或超类(superclass), 继承父类的类叫子类(subclass)或派生类(derivedclass)。 因此, 子类是父类的一个专门用途的版本, 它继承了父类中定义的所有实例变量和方法, 并且增加了独特的元素 。
  实现:
      extends,子类继承父类
  优点:
      1.减少代码量,能很好的提高复用率
      2.使类与类之间存在继承关系,是实现多态操作的前提
  缺点:
      继承使得多个类之间具有了子父类关系,当一个类存在多个子类的时候,如果父类发生变化,那么这些子类会跟着一同变化,造成类与类之间的“强耦合”关系
  • 多态
含义:
               即同一消息可以根据发送对象的不同而采用多种不同的行为方式
               父类型的引用指向子类型的对象
               如: father f=new son()
    实现:
               满足三个条件:有继承、 有重写、 有父类引用指向子类对象
    优点:
      1.可替换性,多态对一存在的代码具有可替代性
      2.可扩充性:增加的子类不影响已存在的类的特性的运行和操作
      3.接口性:多态时超类通过方法签名想子类提供了一个公共的接口,由子类来完善或者覆盖它而实现的
      4.灵活性:在应用中体现了灵活多样的操作,提高了使用的效率
      5.简化性: 多态简化对应用软件的代码的编写和修改过程,尤其在处理大量的对象的运算和操作时,这个特点尤为突出和重要
缺点:
      1.只能使用父类的引用访问父类的成员
      2.成员变量:编译与运行时期都看父类
      3.成员方法:编译时期看父类,运行时期看子类

Collection 和 Collections 的区别

Collection 是集合类的上级接口,继承与他的接口主要有 Set 和 List

Collections 是针对集合类的一个帮助类,他提供一系列静态方法实现对各种集合的搜索、 排序、线程安全化等操作

Object 中有哪些公共方法

Object 是所有类的父类,任何类都默认继承 Object 
- clone 保护方法,实现对象的浅复制, 只有实现了 Cloneable 接口才可以调用该方法,否则抛出 CloneNotSupportedException 异常
- equals 在 Object 中与==是一样的,子类一般需要重写该方法
- hashCode 该方法用于哈希查找,重写了 equals 方法一般都要重写
- getClass final 方法,获得运行时类型 wait 使当前线程等待该对象的锁,当前线程必须是该对象的拥有者,也就是具有该对象的锁
- wait()方法一直等待,直到获得锁或者被中断

方法重载、方法重写他们之间的区别

重载 : 发生在一个类里,方法名相同,参数列表不同,与方法返回值和访问修饰符无关,即重载的方法不能根据返回类型进行区分

重写 : 发生在父子类中,方法名、参数列表必须相同,返回值类型等于父类,抛出的异常小于等于父类,访问修饰符大于等于父类

面向对象设计的原则有哪些

1. 开闭原则:开发的模块必须是开放的,支持扩展的,而不是僵化的。封闭指的是对模块的功能进行扩展时,不应该影响或者大规模地影响以有的程序模块,而是通过增加新类的方式去扩展功能。
2. 里氏替换原则:子类型必须能够替换掉他们的父类型、并出现在父类能够出现的任何地方。LSP原则能让我们正确地设计出合理的对象继承设计、合理地应用抽象机制。
3. 单一职能原则:避免相同的职责分散到不同的类中。避免一个类承担太多的职责。
4. 接口隔离原则:一个类对另外一个类的依赖是建立在一个最小的接口之上的。客户端程序不应该依赖它不需要的接口方法。
5. 依赖倒转原则:上层模块不应该依赖于下层模块,他们共同依赖于一个抽象。抽象不能依赖于具体,具体应该依赖于抽象。
6. 迪米特法则:一个对象应当对其他对象有尽可能少的了解,不和陌生人说话.降低各个对象之间的耦合,提高系统的可维护性。又称最少知识原则。
7. 组合聚合复用原则:尽量使用对象组合,而不是继承来达到复用的目的继承关系是强耦合,组合关系是低耦合。

ThreadLocal的理解和底层结构

1. 本地线程,线程绑定技术。
  简单: 将一个对象存入当前线程对象中,在后续执行流程中,任意位置都可以获得该对象数据,从而实现线程内的数据共享。
  
2. 应用场景
  * service控制事务和dao访问db,使用conn,必须是同一个。
  * mybatis控制事务的sqlsession和访问db的使用sqlsession,必须是同一个。
  * 在filter中,拦截通用的参数,处理完毕后,在后续controller service 任意位置都可以直接获取值,比如分页参数。

3.  ThreadLocal底层数据结构
  是一个map结构
  key:当前threadlocal对象
  value:共享数据对象。 

Map实现类和底层数据结构

# Map实现类
  HashMap
    使用HashSet实现, key无序.
  TreeMap 
    使用Tree实现, key可以进行排序.

  1. HashMap底层使用数组+链表方式实现

  2. HashMap将将要存储的值按照key计算其对应的数组下标,如果对应的数组下标的位置上是没有元素的,那么就将存储的元素存放上去,但是如果该位置上已经存在元素了,那么这就需要用到我们上面所说的链表存储了,将数据按照链表的存储顺序依次向下存储就可以了。

  3. 当HashMap的链表长度超过一个固定值(8),并且数组长度超过64后,会将当前这个链表转化为红黑树.

  4. 对于初始长度为16的数组,当其中存储的数据长度等于16*0.75=12时。就会对数组元素进行扩容,扩容量是原来数组容量的2倍。

集合List Set Map特点

# List集合:
  有序号(从0开始)
  对象可以重复
  有顺序(维持了存入的顺序)
  
  
# Set集合:
  无序号
  对象不可以重复
    注意:
      ① 默认set认为对象的地址是一样的,才是重复对象。对象的属性值一样,不属于重复对象。
      ② set对存入对象判定是否是同一对象的规则:
        先判断对象hashcode是否一样。(类似对象地址)。
        如果hashcode不同, 会继续调用对象equals方法,判定对象是否相同。
      如果希望set认为对象的属性值一样, 就是同一对象只保留1份。
      重写对象hashcode方法和equals
      重写规则: 尽量让相同对象的hashcode相同, 对象属性不一样的对象的equals返回false
      
  无顺序(对象存入set后,顺序混乱)
  
# Map集合
  每个对象Entry,包含key-value
  key不可以重复
  value可以重复
  无顺序

String,StringBuilder,StringBuffer的区别?

# String不可变性
1. 串池中的字符串, 是jvm级别, 多线程共享, 一旦某个线程修改字符串内容, 导致其他线程对于该字符串引用内容, 发生意外修改, 不安全.
2. String对象,在做字符串拼接,修改字符串, 必须新创建一个新的字符串.---(字符串不可变性)
总结: 字符串对象一旦创建, 内容是不会改变.

# 题目答案:
1. String不可变性导致拼接会产生大量中间对象, 浪费时间, 浪费空间 效率降低.
2. StringBuilder可变字符串, 在拼接, 避免中间对象的产生,节省空间, 节省时间. 效率高.   

# 补充
String 字符串常量 不可变 使用字符串拼接时是不同的2个空间
StringBuffer 字符串变量 可变 字符串拼接直接在后面追加 线程安全
StringBuilder 字符串变量 可变 字符串拼接直接在后面追加 非线程安全

equals和==的区别是什么?

# 区别
  1. equals方法:equals比较对象的信息(内容 属性值),是否相同。【关键在于需要在类中重写equals方法】
    注意, 默认一个对象调用的equals是从Object类继承来的,默认内部比较的方式还是==比较地址。
    
  2. == boolean运算符:比较对象的地址是否相同。 

Integer a = 128;Integer b = 128; a == b的结果是什么?

# 常量池:
1. jvm认为数字 -128 ~ 127之间的数字在java程序使用频率是最高。
2. 每次使用数字, 都需在内存中开辟空间(占用空间), 初始化数据(消耗时间)
3. jvm启动时候,会在内存中先初始化一定数量的数字:-128~127之间。
4. 后续java程序运行期间, 凡是需要使用-128~127之间数字的,都直接从常量池获取,减少数字重复占用内存, 减少数字创建等待时间。效率提升了。

包装类和基本数据类型的区别

# 说明
  基本数据类型: byte short int long float double boolean char (8种)
  包装数据类型: Integer Long Byte Short Double Boolean Character
  区别:
  1. 包装类型属于对象类型
  2. 包装类型的默认值是null;
    例子: 
          Double score = null;
          Double score = 0.0; 

反射原理以及使用场景

Java反射:
    是指在运行状态中,对于任意一个类都能够知道这个类所有的属性和方法;并且都能够调用它的任意一个方法;

反射原理:
    反射首先是能够获取到Java中的反射类的字节码,然后将字节码中的方法,变量,构造函数等映射成 相应的 Method、Filed、Constructor 等类

如何得到Class的实例:
  1.类名.class(就是一份字节码)
  2.Class.forName(String className);根据一个类的全限定名来构建Class对象
  3.每一个对象多有getClass()方法:obj.getClass();返回对象的真实类型
  
  
使用场景:
  开发通用框架: 反射最重要的用途就是开发各种通用框架。很多框架(比如 Spring)都是配置化的(比如通过 XML 文件配置 JavaBean、Filter 等),为了保证框架的通用性,需要根据配置文件运行时动态加载不同的对象或类,调用不同的方法。
  动态代理: 在切面编程(AOP)中,需要拦截特定的方法,通常,会选择动态代理方式。这时,就需要反射技术来实现了。
    JDK:spring默认动态代理,需要实现接口
    CGLIB:通过asm框架序列化字节流,可配置,性能差
  自定义注解:注解本身仅仅是起到标记作用,它需要利用反射机制,根据注解标记去调用注解解释器,执行行为。

抽象类和接口

抽象类:包含抽象方法的类,即使用abstract修饰的类;抽象类只能被继承,所以不能使用final修饰,抽象类不能被实例化,
接口:接口是一个抽象类型,是抽象方法的集合,接口支持多继承,接口中定义的方法,默认是public abstract修饰的抽象方法

相同点:
  ① 抽象类和接口都不能被实例化
  ② 抽象类和接口都可以定义抽象方法,子类/实现类必须覆写这些抽象方法

不同点:
  ① 抽象类有构造方法,接口没有构造方法
  ③抽象类可以包含普通方法,接口中只能是public abstract修饰抽象方法(Java8之后可以)
  ③ 抽象类只能单继承,接口可以多继承
  ④ 抽象类可以定义各种类型的成员变量,接口中只能是public static final修饰的静态常量

抽象类的使用场景:
  既想约束子类具有共同的行为(但不再乎其如何实现),又想拥有缺省的方法,又能拥有实例变量

接口的应用场景:
  约束多个实现类具有统一的行为,但是不在乎每个实现类如何具体实现;实现类中各个功能之间可能没有任何联系

Java的四种引用,强弱软虚

# 强引用
  强引用是平常中使用最多的引用,强引用在程序内存不足(OOM)的时候也不会被回收,使用方式:
  String str = new String("str");
# 软引用
  软引用在程序内存不足时,会被回收,使用方式:
  // 注意:wrf这个引用也是强引用,它是指向SoftReference这个对象的, 
  // 这里的软引用指的是指向new String("str")的引用,也就是SoftReference类中T 
  SoftReference<String> wrf = new SoftReference<String>(new String("str"));

  可用场景: 创建缓存的时候,创建的对象放进缓存中,当内存不足时,JVM就会回收早先创建的对象。
# 弱引用
  弱引用就是只要JVM垃圾回收器发现了它,就会将之回收,使用方式:
  WeakReference<String> wrf = new WeakReference<String>(str);
  可用场景: Java源码中的 java.util.WeakHashMap 中的 key 就是使用弱引用,我的理解就是,一旦我不需要某个引用,JVM会自动帮我处理它,这样我就不需要做其它操作。

# 虚引用
  虚引用的回收机制跟弱引用差不多,但是它被回收之前,会被放入 ReferenceQueue 中。注意哦,其它引用是被JVM回收后才被传入 ReferenceQueue 中的。由于这个机制,所以虚引用大多被用于引用销毁前的处理工作。还有就是,虚引用创建的时候,必须带有 ReferenceQueue ,使用
  例子:
  PhantomReference<String> prf = new PhantomReference<String>(new String("str"), new ReferenceQueue<>());
  可用场景: 对象销毁前的一些操作,比如说资源释放等。** Object.finalize() 虽然也可以做,这类动作,但是这个方式即不安全又低效

上诉所说的几类引用,都是指对象本身的引用,而不是指 Reference 的四个子类的引用
( SoftReference 等)。

深拷贝和浅拷贝的区别

浅拷贝:被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用仍然指向原来的对象.换言之,浅拷贝仅仅复制所拷贝的对象,而不复制它所引用的对象.
深拷贝:被复制对象的所有变量都含有与原来的对象相同的值.而那些引用其他对象的变量将指向被复制过的新对象.而不再是原有的那些被引用的对象.换言之.深拷贝把要复制的对象所引用的对象都复制了一遍.

HashSet的底层实现

(1)基于HashMap实现的,默认构造函数是构建一个初始容量为16,负载因子为0.75 的HashMap。封装了一个 HashMap 对象来存储所有的集合元素,所有放入 HashSet 中的集合元素实际上由 HashMap 的 key 来保存,而 HashMap 的 value 则存储了一个 PRESENT,它是一个静态的 Object 对象。

(2)当我们试图把某个类的对象当成 HashMap的 key,或试图将这个类的对象放入 HashSet 中保存时,重写该类的equals(Object obj)方法和 hashCode() 方法很重要,而且这两个方法的返回值必须保持一致:当该类的两个的 hashCode() 返回值相同时,它们通过 equals() 方法比较也应该返回 true。通常来说,所有参与计算 hashCode() 返回值的关键属性,都应该用于作为 equals() 比较的标准。

(3)HashSet的其他操作都是基于HashMap的。

以上是关于毕业季--Java基础面试题的主要内容,如果未能解决你的问题,请参考以下文章

毕业季-Java分布式开发面试题

毕业季-Java分布式开发面试题

毕业季Java面试必备,阿里华为腾讯等公司面试题汇总

毕业季--JavaSE高级面试题

毕业求职季-听说你想去大厂看学姐,带你看看网易java面经

毕业季--JavaSE高级面试题