目录
4、为什么局部内部类和匿名内部类只能访问局部final变量?
5、String、StringBuffer、StringBuilder区别及使用场景
1、什么是面向对象?对面向对象的理解。
对比面向过程,是两种不同的处理问题的角度
面向过程更注重事情的每一个步骤及顺序,面向对象更注重事情有哪些参与者(对象),以及各自需要做的事。
简单来讲就是,面向过程是一个流程化的步骤,而面向对象是在这件事情中有几个参与者(对象),这几个参与者(对象)分别要干什么,有点分门别类的意思,个人理解为行为动作按照主语进行划分。例如洗衣机洗衣服就有两个对象,一个是人,一个是洗衣机。人的动作是打开洗衣机,放衣服,放洗衣粉,而洗衣机的动作是清洗。
面向对象相对于面向过程,更易于复用、扩展和维护,我个人理解是解耦,使得对象与对象之间的关系没有那么密切,均可以进行单独的修改。
面向对象的三大特性:封装性、继承性、多态性。
封装性:封装的意义,在于明确标识出允许外部使用的所有成员函数和数据项。
即内部细节对外部调用透明,外部调用无需修改或者关系内部实现(只需要知道如何调用,不需要知道内部如何实现)。
经典的两场景体现:1、属性私有;2、orm框架。
继承性:继承父类的方法,扩展自己的方法。
多态性:基于对象所属类的不同,外部对同一方法的调用,实际执行的逻辑不同。
继承,方法重写,父类引用指向子类对象。(编译看左,运行看右)
父类类型 变量名 = new 子类对象;
变量名.方法名();
无法调用子类特有功能。
2、JDK、JRE、JVM三者区别和联系
JDK:Java Development Kit java开发工具
JRE:Java Runtime Environment java运行时环境
JVM:Java Virtual Machine java虚拟机
JDK>JRE>JVM
JDK=JRE+java开发工具(javac java jconsole)
JRE=JVM(虚拟机)+java核心类库
3、简述final作用。
final在java中可以用来修饰类、方法以及变量。
修饰类时,表示类不可被继承;
修饰方法时,表示方法不可被子类覆盖(不能重写),但是可以重载;
修饰变量:表示变量一旦被赋值就不可以更改它的值。
在修饰变量中,如果修饰普通的成员变量,那么在声明的时候就需要赋值,或者代码块中赋值,或者构造器赋值;
如果修饰的是静态的成员变量(static),在声明时就要赋值,或者静态代码块赋值;
如果修饰的是局部变量,那么在声明时没有初始化不会报错,但在使用之前一定要赋值,且不允许第二次赋值。
如果修饰的是基本数据类型的变量,那么数值一旦初始化之后就不能改变,即final int a = 1;那么后面就不能够再给a进行赋值;
如果修饰的是引用数据类型的变量,那么其初始化后始终只能指向一个对象,但是对象中的变量是可以更改的(即例如有个数组被final修饰,数组中的内容可以更改,但是不能将该数组赋为null)。
4、为什么局部内部类和匿名内部类只能访问局部final变量?
① 内部类和外部类是处于同一个级别的,内部类不会因为定义在方法中就随着方法的执行完毕就被销毁。这是前提条件。
② 于是出现了问题,即当外部类的方法结束时,按照内存回收机制(GC),局部变量就应该被销毁。但是内部类对象可能还存在(只有在不再被引用时才会死亡)。这里就出现了矛盾:内部类对象访问了一个不存在的变量(内部类中有使用到被回收掉的局部变量)。为了解决这个问题,就将局部变量复制了一份作为内部类的成员变量,这样当局部变量死亡后,内部类仍可以访问它,实际访问的是局部变量的复制,延长了局部变量的生命周期。
③ 在这种情况下,复制的局部变量必须保证与之前的变量一直,因此就将局部变量设置为final,对该局部变量初始化后,就不需要再去修改这个变量,保证了内部类的成员变量和方法局部变量的一致性。
5、String、StringBuffer、StringBuilder区别及使用场景
String在底层源码中是final修饰的,不可变,即每次操作都会产生新的String对象;
StringBuffer和StringBuilder都是在原对象上操作(与String不同);
StringBuffer是线程安全的,StringBuilder是线程不安全的(多线程情况下:1.需不需要额外加锁2.共享变量3.结果不受影响,不会出现并发问题);
StringBuffer的方法都是synchronized修饰的(线程安全的);
性能上:StringBuilder>StringBuffer>String。
在应用场景上,经常需要改变字符串内容时使用StringBuffer和StringBuilder;
优先使用StringBuilder(性能好,但安全性差),多线程使用共享变量时用StringBuffer(线程安全的)。
6、重载和重写的区别
重载:发生在同一个类中,方法名必须相同,参数类型不同、个数不同、顺序不同,方法返回值和方位修饰符可以不同,发生在编译时。(比如在使用idea时,使用一个类中的方法时,会发现同名的方法有很多,但是后面所用的参数类型及个数都不一样)
(注意这个不是重载!!)
重写:发生在子父类中,方法名、参数列表必须相同,返回值范围小于等于父类,抛出的异常范围小于等于父类,访问修饰符范围大于等于父类;如果父类方法访问修饰符为private则子类不能重写该方法。
7、接口和抽象类
① 抽象类可以存在普通成员函数,而接口中只能存在public abstract方法;
② 抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是public static final类型的;
③ 抽象类只能继承一个,接口可以实现多个。
8、List和Set
List:有序,按对象进入的顺序保存对象,可重复,允许多个Null元素对象,可以使用Iterator(迭代器)取出所有元素,再逐一遍历,还可以使用get(int index)获取指定下标的元素
Set:无序,不可重复,最多允许有一个Null对象,取元素时只能用Iterator接口取得所有元素,再逐一遍历各个元素
9、hashCode和equals
hashCode()的作用是获取哈希码,实际上是返回一个int整。这个哈希码的作用是确定该对象在哈希表中的索引位置。hashCode()定义在JDK的Object.java中,java中的任何类都包含有hashCode()函数。散列表存储的是键值对(key-value),其特点为能根据“键”快速的检索出对应的值。这其中就利用到了哈希码。
以“HashSet如何检查重复”为例说明为什么要有hashCode:
对象加入HashSet时,先计算对象的hashCode判断对象加入的位置,看该位置是否有值,如果没有,HashSet会假设对象没有重复出现。如果该位置发现有值,就调用equals方法检查两个对象是否真的相同。如果相同就不加入,不相同就会重新散列到其他位置。这样的方式减少了equals的次数,提高了执行速度。
如果两个对象相等,则hashCode一定相等
两个对象相等,对两个对象分别调用equals方法都返回true
两个对象有相同的hashCode值,也不一定相等
因此,equals方法被覆盖后,hashCode方法也要覆盖
10、ArrayList和LinkedList区别
ArrayList:基于动态数组,连续内存存储,适合下标访问。
扩容机制:因为数组长度固定,超出长度存数据时需要新建数组,然后将老数组的数据拷贝到新数组(底层默认扩容1.5倍)。如果不是尾部插入数据还会涉及到元素的移动(往后复制一份后再插入金元素)。使用尾插法并指定初始大小可以极大提升性能、甚至超过LinkedList(需要创建大量node对象)
LinkedList:基于链表,存储在分散的内存中,适合做数据插入及删除操作,不适合查询(需要逐一遍历)
遍历LinkedList必须使用Iterator,不要试图使用indexOf等返回元素索引,并利用其遍历。使用indexOf对list进行遍历,当结果为空时会遍历整个列表