1、重载和重写的区别
重载:同一类中多个同名方法根据不同的传参来执行不同的处理逻辑;方法名必须相同,参数类型不同、个数不同、顺序不同;返回值类型可以相同也可以不同(因为返回值类型不是方法签名的一部分)
重写:子类对父类的方法的实现过程进行重新编写。方法名,参数列表和返回值类型都不能改变。抛出的异常范围小于等于父类,访问修饰符范围大于等于父类。
什么是方法签名?
答:方法签名,区分不同方法的标示符;方法是由方法名、形参列表、返回值以及方法体构成;方法签名是由方法名与形参列表构成,也就是说,方法名和形参列表可以唯一地确定一个方法,与方法的返回值没有关系
构造器是否可以被重写,是否可以被重载?
答:构造器可以被重载(Overload),不能被重写(Override)
静态方法能否被重写,能否被重载?
答:静态方法不能被重写,可以被重载
静态方法可以被继承。静态方法是类在加载时就被加载到内存中的方法,在整个运行过程中保持不变,因而不能重写;在Java中,如果父类中含有一个静态方法,且在子类中也含有一个返回类型、方法名、参数列表均与之相同的静态方法,那么该子类实际上只是将父类中的该同名方法进行了隐藏,而非重写,可以通过类名.方法名调用被隐藏地方法;换句话说,父类和子类中含有的其实是两个没有关系的方法,它们的行为也并不具有多态性
2、Java面向对象的三大特性
封装:把一个对象的属性私有化,不允许外部对象直接访问这些私有属性,同时提供一些可以被外界访问私有属性的方法
继承:子类继承父类的非私有属性和方法;子类可以对父类的方法进行重写,也可以进行扩展,拥有自己的属性和方法;一个子类只能拥有一个父类,但是可以通过实现多个接口来达到多重继承的目的
多态:同一个操作作用在不同对象时,可以产生不同的执行结果;在Java语言中,多态主要有两种表现形式,方法的重载和重写
多态分为编译时多态-->重载;运行时多态-->重写
- 重载:同一个类中有多个同名方法,根据不同的传参可以执行不同的处理逻辑;在编译时就可以确定到底调用哪个方法,它是一种编译时多态
- 重写:子类对父类的方法的实现过程进行重新编写,方法名,参数列表和返回值类型都不能改变,因此同样的方法在父类与子类中有着不同的表现形式。
Java语言中,父类的引用变量不仅可以指向父类的实例对象,也可以指向子类的实例对象。而程序调用的方法在运行时才动态绑定,就是引用变量所指向的具体实例对象的方法,也就是内存中正在运行的那个对象的方法,而不是引用变量的类型中的定义的方法。这就会出现相同类型的变量调用同一个方法时呈现出多种不同的行为特征,这就是多态;在运行时才能确定调用哪个方法,被称为运行时多态
使用多态的好处?
多态:同一个操作作用在不同对象时,可以产生不同的执行结果;使用多态,可以解决代码的紧耦合的问题,提高程序的可扩展性
- 应用程序不必为每一个子类编写功能调用,只需要对抽象父类进行处理即可,大大提高程序的可复用性
- 子类的功能可以被父类的方法或引用变量所调用,这叫向上兼容,可以提高可扩充性和可维护性
3、Java面向对象的5大设计原则
Java中,所有的异常都有⼀个共同的祖先java.lang包中的Throwable类:Throwable类有两个重要的⼦类:Exception(异常) 和 Error(错误),⼆者都是Java异常体系的重要⼦类,各⾃都包含⼤量⼦类
Error(错误):程序⽆法处理的错误,表示运⾏应⽤程序中较严重问题;⼤多数错误与代码编写者执⾏的操作⽆关,⽽表示代码运⾏时JVM出现的问题;例如,Java 虚拟机运⾏错误(Virtual MachineError),当 JVM 不再有继续执⾏操作所需的内存资源时,将出现内存溢出(OutOfMemoryError);这些异常发⽣时,JVM⼀般会选择终⽌线程
Exception(异常):程序本身可以处理的异常;Exception 类有⼀个重要的⼦类RuntimeException;RuntimeException异常由JVM抛出;还有NullPointerException(要访问的变量没有引⽤任何对象时,抛出该异常);ArithmeticException(算术运算异常,⼀个整数除以0时,抛出该异常)以及 ArrayIndexOutOfBoundsException(数组下标越界异常)
Exception(异常)又分为两类:运行时异常和编译时异常
1、运行时异常(不受检异常):RuntimeException类及其子类表示JVM在运行期间可能出现的错误;比如说试图使用空值对象的引用(NulIPointerException)、数组下标越界(ArraylndexOutBoundException);此类异常属于不可查异常,一般是由程序逻辑错误引起的,在程序中可以选择捕获处理,也可以不处理
2、编译时异常(受检异常):Exception中除RuntimeException及其子类之外的异常;如果程序中出现此类异常,比如说IOException,必须对该异常进行处理,否则编译不通过;在程序中,通常不会自定义该类异常,而是直接使用系统提供的异常类
15、反射
1、什么是Java的反射
反射是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取信息以及动态调用对象的方法的功能称为 Java 语言的反射机制
2、反射的作用
运行时:判断对象所属的类;构造一个对象所属的类;判断一个对象的方法和属性;调用对象的方法和属性
16、Java泛型
Java的泛型即"参数化类型",允许程序在创建集合时指定集合元素的类型,表示该集合只能保存该类型的对象
为什么要使用泛型?
答:如果不使用泛型,当把一个对象存入集合后,集合就会忘记这个对象的数据类型,再次取出该对象时,该对象的编译类型就变成了Object类型,还需要进行强制转换;当使用泛型后,集合中只能存入指定类型的对象,否则将报错,并且将对象从集合取出后无需对元素进行强制转换,就是原本的类型(指定的类型);
由此可见在集合中存储对象并在使用前进行类型转换是多么的不方便;泛型防止了那种情况的发生,提供了编译时的类型安全,确保只能把正确类型的对象放入集合中,避免了在运行时出现ClassCastException(类型转换异常)
扩展知识:
父子对象之间的转换分为了向上转型和向下转型;它们解释如下:
向上转型:通俗地讲,就是将子类对象向上转为父类对象,或者说是父类引用指向子类对象,此处父类对象可以是接口
格式:父类 变量名 = new 子类();
向上转型的好处?
向上转型后,父类引用可以调用子类重写过的父类方法,当需要新添功能时,可以新增一个(子)类即可,而不用更改原父类代码
向上转型后的对象不是新创建的父类对象,而是子类对象的"简化"状态,它不关心子类新增的功能,只关心子类继承和重写的功能;当一个类有很多子类时,并且这些子类都重写了父类中的某个方法,使用上转型对象调用这个方法时就可能具有多种形态,因为不同的子类在重写父类的方法时可能产生不同的行为;也就是说,不同对象的上转型对象调用同一方法可能产生不同的行为
向下转型:与向上转型相反,即是把父类对象转为子类对象;一个已经向上转型的子类对象,将父类引用转为子类引用,可以使用强制转换的格式
格式:子类 变量名 = (父类类型)父类变量;
为什么要向下转型?
向下转型一般是为了重新获得因为向上转型而丢失的子类特性;因此,通常在向下转型前已经有向上转型,而向下转型通常也会结合instanceof一起使用;借由向下转型,可以在灵活应用多态的基础上,同时兼顾子类的独有特征(instanceof:用来测试一个对象是否为一个类的实例)
用法是:boolean result = obj instanceof Class
17、什么是泛型擦除
在代码中定义List<Object>
和List<String>
等类型,在编译后都会变成List,JVM看到的只是List,而由泛型附加的类型信息对JVM是看不到的
在如下例子中,定义了两个ArrayList数组;一个是ArrayList泛型类型的,只能存储字符串;一个是ArrayList泛型类型的,只能存储整数;通过list1对象和list2对象的getClass()方法获取他们的类的信息,最后发现结果为true
说明泛型类型String和Integer都被擦除掉了,只剩下原始类型
Java代码:
import java.util.*;
public class Test {
public static void main(String[] args) {
List<String> l1 = new ArrayList<String>();
List<Integer> l2 = new ArrayList<Integer>();
System.out.println(l1.getClass() == l2.getClass());
System.out.println("l1.getClass():"+l1.getClass().getName());
System.out.println("l1.getClass():"+l2.getClass().getName());
}
}
运行结果:
true
l1.getClass():java.util.ArrayList
l1.getClass():java.util.ArrayList
18、Java Object类中的方法
方法 | 含义 |
---|---|
getClass() | 返回一个对象的运行时类 |
int hashCode() | 返回该对象的哈希码值 |
boolean equals(Object obj) | 判断其他对象是否与此对象“相等” |
String toString() | 返回该对象的字符串表示 |
void notify() | 唤醒在此对象监视器上等待的单个线程 |
void notifyAll() | 唤醒在此对象监视器上等待的所有线程 |
void wait() | 导致当前的线程等待,直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法 |
protected Object clone() | 创建并返回此对象的一个副本 |
protected void finalize() | 当垃圾回收器确定不存在对该对象的更多引用时,由对象的垃圾回收器调用此方法 |
19、Java String类中的方法
20、Java创建对象的5种方式?
①使用new关键字
②使用Class类的newInstance方法
③使用Constructor类的newInstance方法
④使用clone方法
⑤使用反序列化
21、Java访问修饰符的范围
22、Hash冲突的解决方式?
1、开放定址法
当发生冲突时,使用某种探查技术在散列表中形成一个探查序列,沿此序列逐个单元地查找,直到找到一个开放的地址(地址单元为空)或探查序列查找完为止;若探查到开放的地址,则可将待插入的新结点存人该地址单元;若探查序列查找完都没有找到开放的地址,则失败
按照形成探查序列的方法不同,可将开放定址法区分为线性探查法、二次探查法、随机探查法
①线性探查法
②二次探查法
③随机探查法
2.拉链法
将所有哈希地址为i的元素构成一个称为同义词链的单链表,并将单链表的头指针存到哈希表的第i个单元中,因而查找、插入和删除主要在同义词链中进行
拉链法(链地址法)适用于经常进行插入和删除的情况
3.再Hash法
当计算出的hash值产生冲突时,再计算另一个Hash函数的哈希值,直到冲突不再发生为止
4.建立公共溢出区
将哈希表分为基本表和溢出表两部分,凡是和基本表发生冲突的元素,一律填入溢出表
23、Java中的流分为几种?
按照流的流向划分,分为输⼊流和输出流
按照操作单元划分,分为字节流和字符流
按照流的⻆⾊划分,分为节点流和处理流
Java IO流共涉及40多个类,这些类看上去很杂乱,但实际上很有规则,⽽且彼此之间存在⾮常紧密的联系;Java IO流的 40 多个类都是从如下4个抽象类基类中派⽣出来的;
InputStream/Reader: 所有的输⼊流的基类,前者是字节输⼊流,后者是字符输⼊流
OutputStream/Writer: 所有输出流的基类,前者是字节输出流,后者是字符输出流