毕业季--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可以进行排序.
-
HashMap底层使用数组+链表方式实现
-
HashMap将将要存储的值按照key计算其对应的数组下标,如果对应的数组下标的位置上是没有元素的,那么就将存储的元素存放上去,但是如果该位置上已经存在元素了,那么这就需要用到我们上面所说的链表存储了,将数据按照链表的存储顺序依次向下存储就可以了。
-
当HashMap的链表长度超过一个固定值(8),并且数组长度超过64后,会将当前这个链表转化为红黑树.
-
对于初始长度为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基础面试题的主要内容,如果未能解决你的问题,请参考以下文章