一、面向对象相关
1、面向对象三大特征
1、封装:通常认为封装是把数据和操作数据的方法绑定起来,对数据的访问只能通过已定义的接口。
2、继承:继承是从已有类得到继承信息创建新类的过程。提供继承信息的类被称为父类(超类、基类);得到继承信息的类被称为子类(派生类)。
3、多态:多态顾名思义“一种形式多种状态”说了等于白说。其实啥叫多态呢?或许可以这样说:子类对象持有父类或者接口类型的引用,在方法调用期间动态的改变。
2、多态相关
多态分类:
1、编译时多态:方法重载2、运行时多态:方法重写(方法覆盖)
方法重载:都是编译时多态。因为方法重载发生在一个类中。根据实际参数的数据类型、个数和次序,Java在编译时能够确定执行重载方法中的哪一个。编译期就能确定,所以为编译期多态。
方法覆盖:表现出两种多态性,当对象引用本类实例时(发生在一个类中例如:Dog dog = new Dog();dog调用本类方法。即使这个方法是覆盖父类中的。),为编译时多态,否则为运行时多态(Animal animal = new Dog() animal 运行时调用dog中覆盖父类中的方法)。
多态的作用:
消除类型之间的耦合关系。
多态的发生条件:
1、继承
2、重写
3、父类/接口引用指向子类对象
多态好处:
1、可替换性(substitutability)。多态对已存在代码具有可替换性。例如,多态对圆Circle类工作,对其他任何圆形几何体,如圆环,也同样工作。
2、可扩充性(extensibility)。多态对代码具有可扩充性。增加新的子类不影响已存在类的多态性、继承性,以及其他特性的运行和操作。实际上新加子类更容易获得多态功能。例如,在实现了圆锥、半圆锥以及半球体的多态基础上,很容易增添球体类的多态性。
3、接口性(interface-ability)。多态是超类通过方法签名,向子类提供了一个共同接口,由子类来完善或者覆盖它而实现的。
4、活性(flexibility)。它在应用中体现了灵活多样的操作,提高了使用效率。
5、简化性(simplicity)。多态简化对应用软件的代码编写和修改过程,尤其在处理大量对象的运算和操作时,这个特点尤为突出和重要。
3、抽象类和接口区别
1、默认的方法实现
抽象类可以有默认的方法实现。接口完全是抽象的,接口根本不存在方法的实现
2、实现
抽象类的子类使用extends关键字来继承抽象类。如果子类不是抽象类的话,它需要提供抽象类中所有声明的方抽象的方法的实现。接口子类使用关键字implements来实现接口。它需要提供接口中所有声明的方法的实现
3、构造器
抽象类可以有构造器,接口不能有构造器。(构造器就是为了初始化成员变量的)
4、接口中不能定义构造器的理由:
- 构造器用于初始化成员变量,接口没有成员变量
- 类可以实现多个接口,若多个接口都有自己的构造器,则不好决定构造器链的调用次序
- 构造器是属于类自己的,不能继承。因为是纯虚的,接口不需要构造器。
5、和正常java类的区别
抽象类除了你不能实例化抽象类之外,它和普通Java类没有任何区。接口是完全不同的类型
6、访问修饰符
抽象方法可以有public、protected和default这些修饰符。接口中方法默认public,你不可以使用其它修饰符。
7、多继承
抽象类在java语言中所表示的是一种继承关系,一个子类只能存在一个父类,但是可以存在多个接口。
8、速度
抽象类它比接口速度要快,接口是稍微有点慢的,因为它需要时间去寻找在类中实现的方法。
9、添加新方法
如果你往抽象类中添加新的方法,你可以给它提供默认的实现。因此你不需要改变你现在的代码。如果你往接口中添加方法,那么你必须改变实现该接口的类。
4、父类的静态方法能否被子类重写
不能:
1、静态方法和属性是属于类的,调用的时候直接通过类名.方法名完成对,不需要继承机制及可以调用。如果子类里面定义了静态方法和属性,那么这时候父类的静态方法或属性称之为"隐藏"(即使父类的静态成员和方法不是private的,子类中也有一样的静态方法,父类对象引用也调用不了,发生不了多态。所以父类的静态方法对子类来说是不能重写的这叫做隐藏)。如果你想要调用父类的静态方法和属性,直接通过父类名.方法或变量名完成,至于是否继承一说,子类是有继承静态方法和属性,但是跟实例方法和属性不太一样,存在"隐藏"的这种情况。
2、重写指的是根据运行时对象的类型来决定调用哪个方法,而不是根据编译时的类型。
5、方法重载和方法重写区别
二、基础知识相关
1、java中==和equals和hashCode的区别
2、int、char、long各占多少字节数
常见类型所占字节:
byte 1
short 2
char 2
int 4
float 4
long 8
double 8
boolean:理由是虽然编译后1和0只需占用1位空间,但计算机处理数据的最小单位是1个字节,1个字节等于8位,实际存储的空间是:用1个字节的最低位存储,其他7位用0填补
3、int与integer的区别
1、int 是基本类型,直接存数值,而integer是对象,用一个引用指向这个对象
2、int类型的变量初始为0.而Integer类的变量则初始化为null.
3、int和Integer不能够互用,因为他们两种不同的数据类型(集合存数据时可以看出)
4、如果我们定义一个int类型的数,只是用来进行一些加减乘除的运算or作为参数进行传递,那么就可以直接声明为int基本数据类型,但如果要像
对象一样来进行处理,那么就要用Integer来声明一个对象,因为java是面向对象的语言,因此当声明为对象时能够提供很多对象间转换的方式,与一些常用的方法。自认为java作为一们面向对象的语言,我们在声明一个变量时最好声明为对象格式,这样更有利于你对面向对象的理解
4、String、StringBuffer、StringBuilder区别
1、String是final类,底层final char数组实现。不可变字符串序列。String的值是不可变的,这就导致每次对String的操作都会生成新的String对象,这样不仅效率低下,而且大量浪费有限的内存空间。
2、StringBuffer :可变字符串序列,当对字符串修改时使用,线程安全,由于加了锁,导致效率低。
3、StringBuild:可变字符串序列,效率高,不能保证线程安全。
ps:总结:StringBuffer 和StringBuild的区别就是StringBuffer 的一些核心方法上加了锁。如果要操作少量的数据用 String;多线程操作字符串缓冲区下操作大量数据 StringBuffer;单线程操作字符串缓冲区下操作大量数据 StringBuilder。
5、泛型中extends和super的区别
6、进程和线程的区别
进程是操作系统资源分配的基本单位,而线程是任务调度和执行的基本单位。进程包括线程。
7、final,finally,finalize的区别
8、序列化
9、闭包和局部内部类的区别
10、string 转换成 integer的方式及原理
三、优点难度的
1、垃圾回收机制处理
1、那些内存需要被回收:垃圾对象回收之前要先进行对象搜索,然后再进行回收。搜索设计到了两个算法:引用计数算法(已启用)、可达性分析算法。
- 虚拟机栈中引用的对象
- 方法区中静态属性引用的对象
- 方法区中常量引用的对象
- 本地方法栈中JNI应用的对象
2、垃圾回收算法
- 标记—清除算法:包括两个阶段:“标记”和“清除”。在标记阶段,确定所有要回收的对象,并做标记。清除阶
段紧随标记阶段,将标记阶段确定不可用的对象清除。标记—清除算法是基础的收集算法,标记和清除阶段的效率不高,而且清除后回产生大量的不连续空间,这样当程序需要分配大内存对象时,可能无法找到足够的连续空间。
- 复制算法:复制算法是把内存分成大小相等的两块,每次使用其中一块,当垃圾回收的时候,把存活的对象复制到另一块上,然后把这块内存整个清理掉。复制算法实现简单,运行效率高,但是由于每次只能使用其中的一半,造成内存的利用率不高。现在的 JVM 用复制方法收集新生代,由于新生代中大部分对象(98%)都是朝生夕死的,所以两块内存的比例不是 1:1(大概是 8:1)。
- 标记—整理算法:标记—整理算法和标记—清除算法一样,但是标记—整理算法不是把存活对象复制到另一块内存,而是把存活对象往内存的一端移动,然后直接回收边界以外的内存。标记—整理算法提高了内存的利用率,并且它适合在收集对象存活时间较长的老年代。
- 分代收集:分代收集是根据对象的存活时间把内存分为新生代和老年代,根据各个代对象的存活特点,每个代采用不同的垃圾回收算法。新生代采用复制算法,老年代采用标记—整理算法。垃圾算法的实现涉及大量的程序细节,而且不同的虚拟机平台实现的方法也各不相同。
2、常见编码方式
3、静态代理和动态代理的区别,什么场景使用
4、Java 中异常分为哪些种类
按 照 异 常 需 要 处 理 的 时 机 分 为 :
- 编 译 时 异 常 也 叫 CheckedException
- 运 行 时 异 常 也 叫RuntimeException。
只有 java 语言提供了 Checked 异常,Java 认为 Checked 异常都是可以被处理的异常,所以 Java 程序必须显式处理 Checked 异常。如果程序没有处理 Checked 异常,该程序在编译时就会发生错误无法编译。这体现了 Java 的设计哲学:没有完善错误处理的代码根本没有机会被执行。对 Checked 异常处理方法有两种:1 、当前方法知道如何处理该异常,则用 try...catch 块来处理该异常。 2 、当前方法不知道如何处理,则在定义该方法是声明抛出该异常。运行时异常只有当代码在运行时才发行的异常,编译时不需要 try catch。Runtime 如除数是 0 和数组下标越界等,其产生频繁,处理麻烦,若显示申明或者捕获将会对程序的可读性和运行效率影响很大。所以由系统自动检测并将它们交给缺省的异常处理程序。当然如果你有处理要求也可以显示捕获它们。
5、谈谈你对解析与分派的认识
6、深拷贝浅拷贝认识
7、Java深入源码级面试题
四、java数据结构相关
1、常用数据结构简介
1、线性结构:
- 数组、
- 链表:
单循环链表:单向循环链表比单链表多了一个尾节点的指针指向的是头结点. 双向链表:双向链表的每个节点包含以下数据:上一个节点的指针,自己的数据,下一个节点的指针.尾节点没有下一个节点,所以指向null.这样的结构,比如我拿到链表中间的一个节点,即可以往前遍历,也可以往后遍历 双向循环链表:双向循环链表的尾节点的下一个节点是头结点,头节点的上一个节点是尾节点.
- 队列:普通队列、循环队列
- 栈。
2、非线性结构:
- 树:二分搜索树、红黑树、B+树
- 图:无向图、有向图、
- 散列表:也叫作hash表
2、列举java的集合以及集合之间的继承关系
3、常见java集合源码分析
- 看扩容机制:主要在grow方法中,扩容的容量为原来容量的1.5倍(新容量=老容量+老容量>>1,右移一位代表除以2)
- 留意增删的优缺点:增删慢,可能涉及到扩容、元素移位。查找快,数组的天生随机访问优点。
- 底层双向链表实现
- 增删的优缺点:底层链表,插入删除比较快,只需修改要操作元素的指针即可(不考虑遍历时时,链表只需修改插入位置指针,数组还要移动。)。查询慢,需要从头遍历。
- 尾结点的设计:这里为什么要存在一个成员变量尾节点?我感觉是为了方便,比如查找相应索引处元素+插入元素到最后.查找相应索引处元素时,先判断索引是在前半段还是在后半段,如果是在后半段,那么直接从尾节点出发,从后往前进行查找,这样速度更快.在插入元素到最后时,可以直接通过尾节点方便的进行插入.
- 提供了push pop方法,具有栈的功能
- add(int index E element)添加任意位置:这里的思想非常巧妙,如果index在链表的前半部分,那么从first开始往后查找否则,从last往前面查找
- 了解思想即可。大致还是基于单链表的那一套。
?
3、HahsMap源码
hash函数:根据key值,计算出存储地址的位置。 hash函数的设计:常用的是除留余数法:H(key)=key MOD p (p<=m m为表长)很明显,如何选取p是个关键问题。 hash表:哈希表是基于哈希函数建立的一种查找表。 hash冲突:不管hash函数设计的如何巧妙,总会有特殊的key导致hash冲突,特别是对动态查找表来说。 hash函数解决冲突的方法有以下几个常用的方法:
解决hash冲突主要了解:
1、链地址法:产生hash冲突后在存储数据后面加一个指针,指向后面冲突的数据
2、再散列法:准备若干个hash函数,如果使用第一个hash函数发生了冲突,就使用第二个hash函数,第二个也冲突,使用第三个。。。重点了解一下开放定制法和链地址法
- hash表的查找效率影响:
> 1、选用的hash函数 > > 2、选用的处理冲突的方法 > > 3、hash表的饱和度,装载因子 α=n/m(n表示实际装载数据长度 m为表长) > > ps:假设hash表是均匀的,在描述负载因子与查找成功的关系时,选择不同的处理方法会得到不同的数学公式,但是会得到类似的结论: > > 无论哪个公式,装载因子越大,平均查找长度越大,那么装载因子α越小越好?也不是,就像100的表长只存一个数据,α是小了,但是空间利用率不高啊,这里就是时间空间的取舍问题了。通常情况下,认为α=0.75是时间空间综合利用效率最高的情况
- 构造
> 参数容量:默认16 > > 参数负载因子:默认0.75,当负载因子较大时,给hash表扩容的可能性就会减少,所以相对占用空间就会较少,但是每条entry链上(单链表)的元素会相对较多,查询的时间也会增长(时间上较多)。反之相反。所以负载因子是一个时间和空间上折中的说法。自己设计时看自己追求的是时间上还是空间上合理选择即可。一般使用默认0.75即可。
- 注意点:HashMap如何put数据
?
4、LinkedHashMap源码
- LinkedHashMap是一个继承于HashMap,实现Map接口且有可预知迭代顺序的键值对存储列表。与HashMap的不同之处在于LinkedHashMap多维护了一个所有键值对的双向链表,而这个双向链表就定义了迭代顺序,和访问顺序。LinkedHashMap = HashMap + 双向链表。
- lruCache算法的底层数据结构:无非就是对LinkedHashMap的增删改查做了封装。
- 为啥有序
4、二叉树的遍历
深度优先遍历、广度优先遍历实现
网友评论