开始时间:2021/09/23 13:09
源网站:JavaGuide
面向对象三大特征
封装
封装是指把一个对象的状态信息(也就是属性)隐藏在对象内部,不允许外部对对象直接访问对象的内部信息。但是可以提供一些可以被外界访问的方法来操作属性。(将成员变量修饰符设置为private,将修改该成员变量的方法的修饰符设置为public)
继承
不同类型的对象,相互之间经常有一定数量的共同点。同时,每一个对象还定义了额外的特性使他们与众不同。继承是使用已存在的类定义作为基础建立新类的技术,新的类的定义可以增加新的数据或者新的功能,也可以用父类的功能,但不能选择性德继承父类。通过使用继承,可以快速地创建新的类,可以提高代码的重用,程序的可维护性,节省创造新类的时间,提高我们的开发效率。
关于继承有三个要点:
1、子类拥有父类对象所有的属性和方法(包括私有属性和私有方法),但是父类中的私有属性和方法子类是无法访问,只是拥有。
2、子类可以拥有自己的属性和方法,即子类可以对父类进行扩展。
3、子类可以用自己的方式实现父类的方法。
多态
多态,顾名思义,表示一个对象有多种状态。具体表现为父类的引用指向子类的实例。
多态的特点:
对象类型和引用类型之间具有继承(类)/实现(接口)的关系;
引用类型变量发憷的方法调用的到底是哪个类中的方法,必须在程序运行期间才能确定;
多态不能调用“只在子类存在但在父类不存在”的方法;
如果子类重写了父类的方法,真正执行的是子类覆盖的方法,如果子类没有覆盖父类的方法,执行的是父类的方法。
String StringBuffer和StringBulider的区别是什么?String为什么是不可变的?
可变性
简单来说,String类中使用final关键字修饰字符数组来保存字符串,private final char value[],所以String对象是不可变的。
而StringBuilder与StringBuffer都继承自AbstractStringBulider类,在AbstractStringBuilder中也是使用字符串数组保存字符串char[]value 但是没有用final关键字修饰,所以这两种对象都是可变的。
StringBuilder与StringBuffer的构造方法都是调用父类构造方法也就是AbstractStringBuilder实现的。
线程安全性
String中的对象是不可变的,也就可以理解为常量,线程安全。
AbstractStringBuilder是StringBuilder与StringBuffer的公共父类,定义了一些字符串的基本操作,如expandCapacity、append、insert、indexOf等公共方法。StringBuffer对方法加了同步锁或者对调用的方法加了同步锁,所以是线程安全的。stringBuilder并没有对方法进行加同步锁,所以是非线程安全的。
性能
每次对String类型进行改变时,都会生成一个新的String对象,然后将指针指向新的String对象,StringBuffer每次都会对StringBuffer对象本身进行操作,而不是生成新的对象并改变对象引用。
相同情况下使用StringBuilder相比使用StringBuffer仅能获得10%~15%的性能提升,却要承担多线程不安全的风险。
对三者使用总结:
1、操作少量的数据:使用String;
2、单线程操作字符串缓冲区下操作大量数据:使用StringBuilder;
3、多线程操作字符串缓冲区下操作大量数据:使用StringBuffer;
Object类常见方法总结
Object类是一个特殊的类,是所有类的父类,主要提供了以下十一个方法:
getClass();
hashCode();
equals();
clone();
toString();
notify();
notifyAll();
wait(long timeout) throws InterruptedExcepton 暂停线程的执行
wait(long thimeour,int nanos) throws InterruptException 暂停线程的执行,时间单位毫秒
wait() 一直等,不会超时
finalize() throws Throwable 实例被垃圾回收器回收的时候触发的操作
反射
何为反射?
反射赋予了我们在运行时分析类以及执行类中方法的能力。
反射机制的优缺点
优点:可以让代码更加灵活,为各种框架提供开箱即用的功能;
缺点:让我们在运行时有了分析操作类的能力,同样也增加了安全问题,比如可以无视泛型参数的安全检查。另外,反射的灵能也要稍微差点。
反射的应用场景
平时写业务代码,很少会接触到直接使用反射机制的场景。
Spring/Spring Boot、MyBatis等等框架中都大量使用了反射机制。
这些框架中也大量使用了动态代理,而动态代理的实现也依赖反射。
另外,像Java中的一大利器 注解 的实现,也用到了反射。
异常
Java异常类层次结构图
在Java中,所有的异常都有一个共同的祖先:java.lang包中的Throwable类。
Throwable类有两个重要的子类:Exception(异常)和Error(错误)。Exception能被程序本身处理(try-catch),Error是无法处理的(只能尽量避免。)
Exception:程序本身可以处理的异常,可以通过catch来进行捕获。Exception又分为受检查异常(必须处理)和不受检查异常(可以不处理)。
Error:Error属于程序无法处理的错误,我们没法通过catch来进行捕获。例如,Java虚拟机运行错误(Virtual MachineineError)、虚拟机内存不够错误(OutOfMemoryError)、类定义错误(NoClassDefFoundError)等。这些异常发生时,JVM一般会选择线程终止。
受检查异常
Java代码在编译过程中,如果受检查异常没有被catch/throw处理的话,就没办法通过编译。
除了RuntimeExcep及其子类以外,其他的Exception类及其子类都属于受检查异常。常见的受检查异常有:IO相关的异常、classNotFoundException、SQLException...
不受检查异常
Java代码在编译过程中,我们即使不处理不受检查异常也可以正常通过编译。
RuntimeException及其子类都统称为非受检查异常,例如:NullPointerException、NumberFormatException(字符串转换为数字)、ArrayIndexOutOfBoundsException(数字越界)、ClassCastException(类型转换错误)、ArithmeticException(算数错误)等。
Throwable类常用方法
public String getMessage():返回异常发生时的简要描述;
public String toString():返回异常发生时的详细信息;
public String getLocalizedMessage():返回异常对象的本地化信息。使用Throwable的子类覆盖这个方法,可以生成本地化信息。如果子类没有覆盖;
public void printStackTrace():在控制台上打印Throwable对象封装的异常信息。
try-catch-finally
try块:用于捕获异常。其后可接零个或多个catch块,如果没有catch块,则必须跟一个finally块。
catch块:用于处理try块处理到的异常。
finally块:无论是否捕获到异常,finally块的语句都会被执行。当在try块或者catch块中遇到return语句时,finally语句块将在方法返回之前被执行。
在以下3中特殊情况下,finally块不会被执行:
1.在try或finally块中用了System.exit(int)退出程序。但是,如果System.exti(int)在异常语句之后,finally还是会被执行。
2.程序所在的线程死亡。
3.关闭cpu。
使用try-with-resources代替try-catch-finally
I/O流
什么是序列化?什么是反序列化?
如果我们需要持久化Java对象,比如讲Java对象保存在文件中,或者在网络传输Java对象,这些场景都需要用到序列化。
简单来说:
序列化:将数据结构或对象转换成二进制字节流的过程。
反序列化:将在序列化过程中所产生的二进制字节流转换成2数据结构或者对象的过程。
对于Java这种面向对象编程的语言来说,我们序列化的都是对象(object)也就是实例化后的类(class),但是在C++这种半面对对象的语言中,struct(结构体)定义的是数据结构类型,而class对应的是对象类型。
综上:序列化的主要目的是通过网络传输对象或者说是将对象存储到文件系统、数据库、内存中。
Java序列中如果有些字段不想进行序列化,怎么办?
对于不想进行序列化的变量,使用transient关键字修饰。
transient关键字的作用是:阻止实例中那些用此关键字修饰的变量序列化;当对象被反序列化时,被transient修饰的变量值不会被持久化和恢复。
关于transient还有几点注意:
transient只能修饰变量,不能修饰类和方法。
transient修饰的变量,在反序列化后变量值将会被置成类型的默认值。例如,如果是修饰Int类型,那么反序列化后结果就是0。
static变量因为不属于任何对象(object),所以无论有没有transient关键字修饰,均不会被序列化。
获取用键盘输入常用的两种方法
方法1:通过Scanner
Scanner input=new Scanner(System.in);
String s=input.nextLine();
input.close();
方法2:通过BufferedReader
BufferedReader input=new BufferedReader(new InputStreamReader(System.in));
String s=input.readLine();
Java中IO流分为几种?
按照流的流向分,可以分为输入流和输出流;
按照操作单元划分,可以划分为字节流和字符流;
按照流的角色划分为节点流和处理流。
Java IO流共涉及40多个类,这些类看上去很杂乱,但实际上很有规则,而且彼此之间存在非常紧密的联系。JavaIO流的40多个类都是从如下4个抽象类基类中派生出来的。
- InputStream/Reader: 所有的输入流的基类,前者是字节输入流,后者是字符输入流。
- OutputStream/Writer: 所有输出流的基类,前者是字节输出流,后者是字符输出流。
既然有了字节流,为什么还要有字符流?
问题本质想问:不管是文件读写还是网络发送接收,信息最小存储单位都是字节,为什么IO操作要分为字节流操作和字符流操作呢?
回答:字符流是由Java虚拟机将字节转换得到的,转换这个过程非常耗时,并且,如果我们不知道编码类型就很容易出现乱码问题。所以,IO流就干脆提供了一个直接操作字符的接口,方便我们平时对字符进行流操作。如果音频文件、图片文件等媒体文件用字节流比较好,如果涉及到字符的话使用字符流比较好。