1、方法重写与方法重载的区别:

方法重载:是指在同一个类中的多个方法有相同的名称,但是方法签名不同,编译器能够根据方法签名决定调用哪个方法。方法签名中包含方法名和参数,而方法重载是相当于多个相同的方法名使用不同的参数列表

方法重写:发生在子类和父类之间,子类继承父类的方法并重写父类方法。==重写方法的参数列表和返回值类型必须与被重写的方法保持一致。==如果父类方法访问修饰符为private/final/static,则子类不能重写该方法

区别:



2、JVM vs JDK vs JRE

Java 虚拟机(JVM)是运行 Java 字节码的虚拟机。JVM 有针对不同系统的特定实现(Windows,Linux,macOS),目的是使用相同的字节码,它们都会给出相同的结果。字节码和不同系统的 JVM 实现是 Java 语言“一次编译,随处可以运行”的关键所在。

JDK 是 Java Development Kit 缩写,它是功能齐全的 Java SDK。它拥有 JRE 所拥有的一切,还有编译器(javac)和工具(如 javadoc 和 jdb)。它能够创建和编译程序。

JRE 是 Java 运行时环境。它是运行已编译 Java 程序所需的所有内容的集合,包括 Java 虚拟机(JVM),Java 类库,java 命令和其他的一些基础构件。但是,它不能用于创建新程序。



3、自增、自减运算符(++、- -)

++ 和 – 运算符可以放在变量之前,也可以放在变量之后,当运算符放在变量之前时(前缀),先自增/减,再赋值;当运算符放在变量之后时(后缀),先赋值,再自增/减。
b=++a ----》先自增(a=a+1),在赋值(赋值给b)
b=a++ ----》先赋值(赋值给b),在自增(a=a+1)



4、continue、break 和 return 的区别是什么?

continue:跳出当前这一次循环,继续下一次循环

break:跳出整个循环体,执行循环后续内容

return:跳出所在方法,结束该方法的运行



5、静态方法和实例方法有何不同?

调用方式:调用静态方法不需要创建对象,直接使用 类名.方法名的方式;而实例方法需要先对对象进行实例化,通过对象.方法名的方式调用

访问限制:静态方法在访问本类的成员时,只允许访问静态成员(即静态成员变量和静态方法),不允许访问实例成员(即实例成员变量和实例方法),而实例方法不存在这个限制。



6、基本数据类型

注意:
1、Java 里使用 long 类型的数据一定要在数值后面加上 L,否则将作为整型解析。
2、char a = 'h’char :单引号,String a = “hello” :双引号。

这八种基本类型都有对应的包装类分别为:Byte、Short、Integer、Long、Float、Double、Character、Boolean 。



7、什么是自动拆装箱?

装箱:将基本类型用它们对应的引用类型包装起来;

拆箱:将包装类型转换为基本数据类型;



8、面向对象和面向过程

面向对象:将问题分解成对象,描述事物在解决问题的步骤中的行为。对象与属性和行为是关联的

面向过程:将问题分解成步骤,然后按照步骤实现函数,执行时依次调用函数。数据和对数据的操作时分离的。

对象的相等和引用相等的区别
对象的相等一般比较的是内存中存放的内容是否相等。
引用相等一般比较的是他们指向的内存地址是否相等。

类的构造方法的作用是什么?
构造方法是一种特殊的方法,主要作用是完成对象的初始化工作。

面向对象三大特征
封装:封装是指把一个对象的状态信息(也就是属性)隐藏在对象内部,不允许外部对象直接访问对象的内部信息。但是可以提供一些可以被外界访问的方法来操作属性。
继承:继承是使用已存在的类的定义作为基础建立新类的技术,新类的定义可以增加新的数据或新的功能,也可以用父类的功能,但不能选择性地继承父类。通过使用继承,可以快速地创建新的类,可以提高代码的重用,程序的可维护性,节省大量创建新类的时间 ,提高我们的开发效率。
关于继承如下 3 点请记住:
1)子类拥有父类对象所有的属性和方法(包括私有属性和私有方法),但是父类中的私有属性和方法子类是无法访问,只是拥有。
2)子类可以拥有自己属性和方法,即子类可以对父类进行扩展。
3)子类可以用自己的方式实现父类的方法
多态:表示一个对象具有多种的状态,具体表现为父类的引用指向子类的实例。
多态的特点:
1)对象类型和引用类型之间具有继承(类)/实现(接口)的关系;
2)引用类型变量发出的方法调用的到底是哪个类中的方法,必须在程序运行期间才能确定;
3)多态不能调用“只在子类存在但在父类不存在”的方法;
4)如果子类重写了父类的方法,真正执行的是子类覆盖的方法,如果子类没有覆盖父类的方法,执行的是父类的方法
好处:提高了程序的扩展性。定义方法的时候,使用父类型作为参数,将来在使用的时候,使用具体的子类型参与操作
弊端:不能使用子类特有功能,只能访问子类和父类的共有方法



9、接口和抽象类有什么共同点和区别

共同点:
1)抽象类和接口都不能被实例化
2)都可以包含抽象方法
3)都可以有默认的时间方法(JDK1.8的新特性,使用default定义默认方法)

区别:
1)接口主要对于类的行为进行约束,实现了某个接口就具有了对应的行为;抽象类主要用于代码的复用,强调的是所属关系
2)类与类之间是继承关系,一个类只能继承一个类;类与接口是实现关系,一个类可以实现多个接口
3)接口的成员变量只能是 public static final 类型的,不能被修改且必须有初始值;抽象类的成员变量默认default,可以在子类中被重新定义,也可被重新赋值



10、== 和 equals() 的区别

== 可以比较基本数据类型和引用数据类型。对于基本数据类型来说其比较的是指;对于引用数据类型来说,其比较的是对象的内存地址

equals() 不能用于判断基本数据类型的变量,只能用来判断两个对象是否相等。equals()方法存在于Object类中,而Object类是所有类的直接或间接父类,因此所有的类都有equals()方法。



11、String、StringBuffer、StringBuilder 的区别

线程安全性:String 中的对象是不可变的(String类中使用final关键之修饰来保存字符串,因此是不可变的),也就可以理解为常量,线程安全。AbstractStringBuilder 是 StringBuilder 与 StringBuffer 的公共父类,定义了一些字符串的基本操作,如 expandCapacity、append、insert、indexOf 等公共方法。StringBuffer 对方法加了同步锁或者对调用的方法加了同步锁,所以是线程安全的。StringBuilder 并没有对方法进行加同步锁,所以是非线程安全的。


操作少量的数据: 适用 String
单线程操作字符串缓冲区下操作大量数据: 适用 StringBuilder
多线程操作字符串缓冲区下操作大量数据: 适用 StringBuffer



12、字符串拼接使用“+”与StringBuilder的区别

Java 语言本身并不支持运算符重载,“+”和“+=”是专门为 String 类重载过的运算符,也是 Java 中仅有的两个重载过的运算符。通过"+“可以实现字符串拼接,其原理实际上就是通过StringBuilder调用append()方法实现的,拼接完成后调用ToString()方法得到字符串对象。
使用“+”拼接字符串的缺点:对于循环内使用”+"拼接字符串,编译器不会创建单个StringBuilder对象进行复用,会导致创建过多的StringBulider对象



13、String#equal()和Object#equal()方法区别:

String 中的 equals 方法是被重写过的,比较的是 String 字符串的值是否相等。 Object 的 equals 方法是比较的对象的内存地址



14、Java程序异常分类

在 Java 中,所有的异常都有一个共同的祖先 java.lang 包中的 Throwable 类。Throwable 类有两个重要的子类:

Exception :程序本身可以处理的异常,可以通过 catch 来进行捕获。Exception 又可以分为 Checked Exception (受检查异常,必须处理) 和 Unchecked Exception (不受检查异常,可以不处理)。

Error :Error 属于程序无法处理的错误 ,不建议通过catch捕获 。需要程序员手动对错误进行修改



15、Checked Exception 和 Unchecked Exception 有什么区别?

Checked Exception 即 受检查异常 ,Java 代码在编译过程中,如果受检查异常没有被 catch或者throws 关键字处理的话,就没办法通过编译;除了RuntimeException及其子类以外,其他的Exception类及其子类都属于受检查异常 。常见的受检查异常有: IO 相关的异常、ClassNotFoundException 、SQLException

Unchecked Exception 即 不受检查异常 ,Java 代码在编译过程中 ,我们即使不处理不受检查异常也可以正常通过编译;RuntimeException 及其子类都统称为非受检查异常,常见的有(建议记下来,日常开发中会经常用到):
NullPointerException(空指针错误)
IllegalArgumentException(参数错误比如方法入参类型错误)
NumberFormatException(字符串转换为数字格式错误,IllegalArgumentException的子类)
ArrayIndexOutOfBoundsException(数组越界错误)
ClassCastException(类型转换错误)
ArithmeticException(算术错误)
SecurityException (安全错误比如权限不够)
UnsupportedOperationException(不支持的操作错误比如重复创建同一用户)



16、try-catch-finally 如何使用?

try块 : 用于捕获异常。其后可接零个或多个 catch 块,如果没有 catch 块,则必须跟一个 finally 块。

catch块 : 用于处理 try 捕获到的异常。

finally 块 : 无论是否捕获或处理异常,finally 块里的语句都会被执行。当在 try 块或 catch 块中遇到 return 语句时,finally 语句块将在方法返回之前被执行。
finally不被执行的几种情况finally之前虚拟机被终止运行
程序所在线程死亡
关闭CPU



17、什么是泛型?有什么作用?

Java 泛型(Generics) 是 JDK 5 中引入的一个新特性。使用泛型参数,可以增强代码的可读性以及稳定性。编译器可以对泛型参数进行检测,并且通过泛型参数可以指定传入的对象类型。



18、集合详解

Java 集合, 也叫作容器,主要是由两大接口派生而来:一个是 Collection接口,主要用于存放单一元素;另一个是 Map 接口,主要用于存放键值对。


List(对付顺序的好帮手): 存储的元素是有序的、可重复的。
Set(注重独一无二的性质): 存储的元素是无序的、不可重复的。
Queue(实现排队功能的叫号机): 按特定的排队规则来确定先后顺序,存储的元素是有序的、可重复的。
Map(用 key 来搜索的专家): 使用键值对(key-value)存储,类似于数学上的函数 y=f(x),“x” 代表 key,“y” 代表 value,key 是无序的、不可重复的,value 是无序的、可重复的,每个键最多映射到一个值


List:有序可重复集合,List集合是有索引的,可以控制每个元素的插入位置以及使用索引访问元素;
List集合常用子类:ArrayList、LinkedList
ArrayList:底层数据结构是数组,查询快、增删慢;ArrayList底层为数组;
LinkedList:底层数据结构是双向链表,查询慢、增删快;LinkedList底层为双向链表


set集合:不可重复集合;
HashSet集合特点:无序,唯一;基于HashMap实现的,底层采用HashMap来保存元素
LinkedListHashSet:LinkedListHashSet是HashSet的子类,其内部通过LinkedListHashMap来实现
TreeSet:红黑树(自平衡的排序二叉树),底层是TreeMap,添加的数据存入了Map的key位置,而Value则是固定的present


Map集合:kry+value结构,键值对中的键是唯一的
HashMap:由数组+链表+红黑树组成
TreeMap: 红黑树(自平衡的排序二叉树)


集合的选用:
主要根据集合的特点来选用。当需要根据键值对获取元素时,就使用Map接口下的集合,需要排序选用TreeMap,不需要排序选择HashMap;当只存放元素时选择Collection接口的集合,保证元素唯一性选用Set接口的集合如TreeSet和HashSet,不需要则选择实现List接口的集合如ArrayList或LinkedList。

ArrayList 与 LinkedList 区别
线程安全:两者都是非线程安全的
底层数据结构:ArrayList底层使用Object数组;LinkedList底层使用双向链表。
插入和删除:ArrayList 采用数组存储,所以插入和删除元素的时间复杂度受元素位置的影响,根据插入或删除的元素位置,后续的元素需要后移/前移一位操作,时间复杂度0(n-i);LinkedList 采用链表存储,所以,如果是在头尾插入或者删除元素不受元素位置的影响(add(E e)、addFirst(E e)、addLast(E e)、removeFirst() 、 removeLast()),时间复杂度为 O(1),如果是要在指定位置 i 插入和删除元素的话(add(int index, E element),remove(Object o)), 时间复杂度为 O(n) ,因为需要先移动到指定位置再插入。
访问速度:ArrayList通过数组实现,因此可以通过索引进行快速访问;而LinkedList底层是双向链表,不支持高效的随机元素访问,进行访问时需要遍历查找。
空间占用:rrayList 的空 间浪费主要体现在在 list 列表的结尾会预留一定的容量空间,而 LinkedList 的空间花费则体现在它的每一个元素都需要消耗比 ArrayList 更多的空间(因为要存放直接后继和直接前驱以及数据)。


ArrayList底层是素组队列,相当于动态数组,它继承于 AbstractList ,实现了 List, RandomAccess, Cloneable, java.io.Serializable 这些接口。
RandomAccess 是一个标志接口,表明实现这个这个接口的 List 集合是支持快速随机访问的。在 ArrayList 中,我们即可以通过元素的序号快速获取元素对象,这就是快速随机访问。
ArrayList 实现了 Cloneable 接口 ,即覆盖了函数clone(),能被克隆。
ArrayList 实现了 java.io.Serializable接口,这意味着ArrayList支持序列化,能通过序列化去传输。
ArrayList扩容机制:无参构造创建ArrayList时,底层初始化一个空数组,当调用add方法向数组添加元素时,就会默认设定将数组扩容为10;当添加元素超过10个,此时数组空间不足以容纳数组元素,此时就会调用grow()方法利用位运算符(>>)对数组进行扩容,扩容后的数组变为原来1.5倍左右


比较 HashSet、LinkedHashSet 和 TreeSet 三者的异同
HashSet、LinkedHashSet 和 TreeSet 都是 Set 接口的实现类,都能保证元素唯一,并且都不是线程安全的。
HashSet、LinkedHashSet 和 TreeSet 的主要区别在于底层数据结构不同。HashSet 的底层数据结构是哈希表(基于 HashMap 实现)。LinkedHashSet 的底层数据结构是链表和哈希表,元素的插入和取出顺序满足 FIFO。TreeSet 底层数据结构是红黑树,元素是有序的,排序的方式有自然排序和定制排序。
底层数据结构不同又导致这三者的应用场景不同。HashSet 用于不需要保证元素插入和取出顺序的场景,LinkedHashSet 用于保证元素的插入和取出顺序满足 FIFO 的场景,TreeSet 用于支持对元素自定义排序规则的场景。


HashMap 和 Hashtable 的区别
线程是否安全: HashMap 是非线程安全的,Hashtable 是线程安全的,因为 Hashtable 内部的方法基本都经过synchronized 修饰。(如果你要保证线程安全的话就使用 ConcurrentHashMap 吧!);
效率: 因为线程安全的问题,HashMap 要比 Hashtable 效率高一点。另外,Hashtable 基本被淘汰,不要在代码中使用它;
对 Null key 和 Null value 的支持: HashMap 可以存储 null 的 key 和 value,但 null 作为键只能有一个,null 作为值可以有多个;Hashtable 不允许有 null 键和 null 值,否则会抛出 NullPointerException。


HashMap 可以存储 null 的 key 和 value,但 null 作为键只能有一个,null 作为值可以有多个;HashMap 默认的初始化大小为 16。之后每次扩充,容量变为原来的 2 倍,因此HashMap的大小总是2的幂次方。
JDK1.8 以后的 HashMap 在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认为 8)(将链表转换成红黑树前会判断,如果当前数组的长度小于 64,那么会选择先进行数组扩容,而不是转换为红黑树)时,将链表转化为红黑树,以减少搜索时间
Hash提供了put()方法用于添加元素、get()方法用于获取键所对应的值、size()用于查询集合大小、isEmpty()判定是否为空、remove()用于移除某些元素、containKey()和containValue()分别用于判定键和值是否存在


获取所有键值对的两种方法:
1)Set<String> keys = map.keySet(),获取key的集合,在根据集合进行遍历,使用map.get(key)获取键所对应的值,该方法需要进行两次遍历,时间复杂度高
2)Set<java.util.Map.Entry<String, String>> entrys = map.entrySet();,获取数组中所有Entry对象,调用对象中的getKey()和getValue()获取所有键值信息,该方法只需要进行一次遍历

HashMap 和 HashSet 区别:HashSet 底层就是基于 HashMap 实现的
实现接口不同,存储类型不同,调用方法不同,哈希编码计算方式不同


HashMap 和 TreeMap 区别:前者无序后者有序;
TreeMap 和HashMap 都继承自AbstractMap ,但是需要注意的是TreeMap它还实现了NavigableMap接口和SortedMap 接口。实现 NavigableMap 接口让 TreeMap 有了对集合内元素的搜索的能力,实现SortedMap接口让 TreeMap 有了对集合中的元素根据键排序的能力。


集合遍历的几种方式:迭代器Iterator、增强for(foreach)、传统for
在foreach的底层中依赖迭代器Iterator,不要在foreach循环中对元素进行remove/add操作,remove元素请使用 Iterator 方式,否则就会出现有些元素被莫名其妙删除。

LinkedList<String> list = new LinkedList<>();//遍历方式1 迭代器IteratorIterator<String> it = list.iterator();while (it.hasNext()) {//遍历中执行的操作}//foreach(增强for)for (String l:list){//遍历中执行的操作}//传统遍历for (int i=0; i<list.size();i++){//遍历中执行的操作}


19、语法糖

语法糖:指的是在计算机语言中使用某种语法,但该语法对功能并没有影响,只是方便程序员开发使用。但是java虚拟机并不支持这些语法糖,因此在编译阶段会进行解语法糖的操作,使语法糖还原为简单的基础语法结构。
常见语法糖:Java 中最常用的语法糖主要有泛型、自动拆装箱、变长参数、枚举、内部类、增强 for 循环、try-with-resources 语法、lambda 表达式等



20、反射详解

java反射机制:是指在运行时获取一个类的变量和方法信息。然后通过获取到的信息来创建对象,调用方法的一种机制。由于这种动态性,可以极大增强程序灵活性,程序不用在编译期完成确定。
1、反射机制首先要获取类的字节码文件(.class),获取字节码文件有一下几种方法:
1)在知道类的具体情况下,可以使用 类名.class 的方式获取字节码文件
2)先对对象进行实例化,通过对象实例调用 xxx.getClass() 方法获取
3)使用Class类中的静态方法 forName()传入类的全路径获取



21、值传递和引用传递,为啥java中只有值传递


值传递 :方法接收的是实参值的拷贝,会创建副本
引用传递 :方法接收的直接是实参所引用的对象在堆中的地址,不会创建副本,对形参的修改将影响到实参。


java中将实参传递给方法的方式是值传递:
如果参数是基本类型的话,很简单,传递的就是基本类型的字面量值的拷贝,会创建副本。
如果参数是引用类型,传递的就是实参所引用的对象在堆中地址值的拷贝,同样也会创建副本



22、代理模式

代理模式:使用代理对象来代替对真实对象(real object)的访问,这样就可以在不修改原目标对象的前提下,提供额外的功能操作,扩展目标对象的功能。
代理模式的主要作用是扩展目标对象的功能,比如说在目标对象的某个方法执行前后你可以增加一些自定义的操作。

1)静态代理:静态代理中,我们对目标对象的每个方法的增强都是手动完成的(后面会具体演示代码),非常不灵活(比如接口一旦新增加方法,目标对象和代理对象都要进行修改)且麻烦(需要对每个目标类都单独写一个代理类)

2)动态代理:从 JVM 角度来说,动态代理是在运行时动态生成类字节码,并加载到 JVM 中的。Spring AOP、RPC 框架的实现都依赖了动态代理



23、API和SPI


API:当实现方提供了接口和实现,我们可以通过调用实现方的接口从而拥有实现方给我们提供的能力,这就是 API ,这种接口和实现都是放在实现方的。
SPI:当接口存在于调用方这边时,就是 SPI ,由接口调用方确定接口规则,然后由不同的厂商去根据这个规则对这个接口进行实现,从而提供服务。

序列化与反序列化,序列化的主要目的是通过网络传输对象或者说是将对象存储到文件系统、数据库、内存中。
序列化: 将数据结构或对象转换成二进制字节流的过程
反序列化:将在序列化过程中所生成的二进制字节流转换成数据结构或者对象的过程



24、I/O


I/O: Input/Output,输入和输出。IO 流在 Java 中分为输入流和输出流,而根据数据的处理方式又分为字节流和字符流。Java IO 流的 40 多个类都是从如下 4 个抽象类基类中派生出来的:
InputStream/Reader: 所有的输入流的基类,前者是字节输入流,后者是字符输入流。
OutputStream/Writer: 所有输出流的基类,前者是字节输出流,后者是字符输出流。


InputStream(字节输入流):从源头开始读取数据(字节信息)到内存中,java.io.InputStream抽象类是所有字节输入流的父类。
常用方法:
1)read() :返回输入流中下一个字节的数据。如果未读取任何字节,则代码返回 -1 ,表示文件结束。
2)read(byte b[ ]) : 从输入流中读取一些字节存储到数组 b 中。如果数组 b 的长度为零,则不读取。如果没有可用字节读取,返回 -1。如果有可用字节读取,则最多读取的字节数最多等于 b.length , 返回读取的字节数。这个方法等价于 read(b, 0, b.length)
3)read(byte b[], int off, int len) :在read(byte b[ ]) 方法的基础上增加了 off 参数(偏移量用于指定开始写入)和 len 参数(要读取的最大字节数)
4)skip(long n) :忽略输入流中的 n 个字节 ,返回实际忽略的字节数。
5)available() :返回输入流中可以读取的字节数。
6)close() :关闭输入流释放相关的系统资源。
常用的两个子类FileInputStream(文件字节输入流)、BufferedInputStream(字节缓冲输入流)


OutputStream(字节输出流):将数据(字节信息)写入到目的地(通常是文件),java.io.OutputStream抽象类是所有字节输出流的父类。
常用方法:
1)write(int b) :将特定字节写入输出流。
2)rwrite(byte b[ ]) : 将数组b 写入到输出流,等价于 write(b, 0, b.length) 。
3)write(byte[] b, int off, int len) : 在write(byte b[ ]) 方法的基础上增加了 off 参数(偏移量)和 len 参数(要读取的最大字节数)。
4)flush() :刷新此输出流并强制写出所有缓冲的输出字节。
5)close() :关闭输入流释放相关的系统资源。
常用两个字节输出流:FileOutputStream(文件字节输出流)是最常用的字节输出流对象,可直接指定文件路径,可以直接输出单字节数据,也可以输出指定的字节数组。BufferedOutputStream(字节缓冲输出流)


重点:字节流与字符流
信息的最小存储单元为字节。字节流与字符流的区别在于:字节流处理单元为一个字节(byte),字符流处理单元为2个字节的Unicode字符。
I/O流分为字节流和字符流的原因:
1)当编码类型不知道时,使用字节流很容易出现乱码现象
2)字符流是有java虚拟机将字节转换得到的,耗时较长


Reader(字符输入流):用于从源头(通常是文件)读取数据(字符信息)到内存中,java.io.Reader抽象类是所有字符输入流的父类。(Reader—>读取字符;InputStream -->读取字节)
常用方法:
1)read() : 从输入流读取一个字符
2)read(char[] cbuf) : 从输入流中读取一些字符,并将它们存储到字符数组 cbuf中,等价于 read(cbuf, 0, cbuf.length) 。
3)read(char[] cbuf, int off, int len) :在read(char[] cbuf) 方法的基础上增加了 off 参数(偏移量)和 len 参数(要读取的最大字节数)
4)close() : 关闭输入流并释放相关的系统资源。
InputStreamReader 是字节流转换为字符流的桥梁,其子类 FileReader 是基于该基础上的封装,可以直接操作字符文件。


Writer(字符输出流)用于将数据(字符信息)写入到目的地(通常是文件),java.io.Writer抽象类是所有字节输出流的父类
常用方法:
1)write(int c) : 写入单个字符。
2)write(char[] cbuf) :写入字符数组 cbuf,等价于write(cbuf, 0, cbuf.length)。
3)write(char[] cbuf, int off, int len) :在write(char[] cbuf) 方法的基础上增加了 off 参数(偏移量)和 len 参数(要读取的最大字节数)。
4)write(String str) :写入字符串,等价于 write(str, 0, str.length())。
5)write(String str, int off, int len) :在write(String str) 方法的基础上增加了 off 参数(偏移量)和 len 参数(要读取的最大字节数)。
6)flush() :刷新此输出流并强制写出所有缓冲的输出字符。
7)close():关闭输出流释放相关的系统资源。
OutputStreamWriter 是字符流转换为字节流的桥梁,其子类 FileWriter 是基于该基础上的封装,可以直接将字符写入到文件


字节缓冲流:BufferedInputStream(字节缓冲输入流)/BufferedOutputStream(字节缓冲输出流)-----》提高流的传输效率
缓冲流的作用:由于I/O操作时很消耗性能的,缓冲流通过将数据加载至缓冲区域(实际就是一个字节数组),一次性读取/写入多个字节,从而避免频繁的I/O操作,提高流的传输效率。
字节缓冲流这里采用了装饰器模式来增强 InputStream 和OutputStream子类对象的功能。
字节缓冲输入流/字节缓冲输出流通常都是和字节输入流/字节输出流(或子类)结合使用的,流的输入输出方法基本一致

BufferedInputStream bis = new BufferedInputStream(new FileInputStream("深入理解计算机操作系统.pdf"));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("深入理解计算机操作系统-副本.pdf"))


字符缓冲流:BufferedReader(字符缓冲输入流)/BufferedWriter(字符缓冲输出流)
BufferedReader (字符缓冲输入流)和 BufferedWriter(字符缓冲输出流)类似于 BufferedInputStream(字节缓冲输入流)和BufferedOutputStream(字节缓冲输入流),内部都维护了一个字节数组作为缓冲区。


打印流:System.out 实际是用于获取一个 PrintStream 对象,print方法实际调用的是 PrintStream 对象的 write 方法,从而实现对流的打印输出。print和println的区别在于是否输出后进行跳行
PrintStream 属于字节打印流,与之对应的是 PrintWriter (字符打印流)。PrintStream 是 OutputStream 的子类,PrintWriter 是 Writer 的子类。

System.out.println();
System.out.print();


25、I/O设计模式


装饰器模式:装饰器(Decorator)模式 可以在不改变原有对象的情况下拓展其功能。
装饰器模式通过组合替代继承来扩展原始类的功能,在一些继承关系比较复杂的场景更加实用。不如IO中的输入流和输出流的子类太多,继承关系过于复杂,如果对每个子类都设定一个对应的字符缓冲流将会带来极大的麻烦。因此采用了装饰器模在原有功能上进行拓展,这也是BufferedInputStream、BufferedOutputStream、BufferedReadder、BufferedWrite要结合出入输出流或其子类进行使用的原因。
例子:通过适配器,我们可以将字节流对象适配成一个字符流对象,这样我们可以直接通过字节流对象来读取或者写入字符数据。


适配器模式:主要用于接口互不兼容的类的协调工作,让接口不兼容而不能交互的类可以一起工作。
适配器模式中存在被适配的对象或者类称为 适配者(Adaptee) ,作用于适配者的对象或者类称为适配器(Adapter) 。适配器分为对象适配器和类适配器。类适配器使用继承关系来实现,对象适配器使用组合关系来实现。


java中常见的3中IO模型:
1)同步阻塞IO模型:应用程序发起 read 调用后,会一直阻塞,直到内核把数据拷贝到用户空间;这种模型的缺点是无法解决高并发量的数据处理

2)非同步阻塞模型:同步非阻塞 IO 模型中,应用程序会一直发起 read 调用,等待数据从内核空间拷贝到用户空间的这段时间里,线程依然是阻塞的,直到在内核把数据拷贝到用户空间。相比于同步阻塞 IO 模型,同步非阻塞 IO 模型确实有了很大改进。通过轮询操作,避免了一直阻塞。缺点是不断进行IO轮询十分消耗CPU资源。
Java 中的 NIO 于 Java 1.4 中引入,对应 java.nio 包,提供了 Channel , Selector,Buffer 等抽象。NIO 中的 N 可以理解为 Non-blocking,不单纯是 New。它是支持面向缓冲的,基于通道的 I/O 操作方法。 对于高负载、高并发的(网络)应用,应使用 NIO 。
Java 中的 NIO 可以看作是 I/O 多路复用模型。也有很多人认为,Java 中的 NIO 属于同步非阻塞 IO 模型。

3)IO多路复用模型:IO 多路复用模型中,线程首先发起 select 调用,询问内核数据是否准备就绪,等内核把数据准备好了,用户线程再发起 read 调用。read 调用的过程(数据从内核空间 -> 用户空间)还是阻塞的。IO 多路复用模型,通过减少无效的系统调用,减少了对 CPU 资源的消耗



26、并发编程

多线程的两种实现方案:
1、继承Thread类----》在继承类中重写run方法-----》创建继承方法的对象------》对象.start() 启动线程
2、实现Runnable接口-----》接口实现类中重写run方法-----》创建实现类对象-----》对象.srart()启动线程


进程和线程:
1)进程:进程是程序的一次执行过程,是系统运行程序的基本单位,因此进程是动态的。系统运行一个程序即是一个进程从创建,运行到消亡的过程。
2)线程:线程是一个比进程更小的执行单位。一个进程在其执行的过程中可以产生多个线程。与进程不同的是同类的多个线程共享进程的堆和方法区资源,但每个线程有自己的程序计数器、虚拟机栈和本地方法栈,所以系统在产生一个线程,或是在各个线程之间作切换工作时,负担要比进程小得多,也正因为如此,线程也被称为轻量级进程。

一个进程中可以有多个线程,多个线程共享进程的堆和方法区 (JDK1.8 之后的元空间)资源,但是每个线程有自己的程序计数器、虚拟机栈 和 本地方法栈。


并发与并行:
1)并发:两个及两个以上的作业在同一时间段内执行;
2)并行:两个及两个以上的作业在同一时刻执行


使用多线程的优势:
1)线程是程序执行的最小单位,线程间的切换和调度成本远小于进程;且在CPU中多个线程可以同时运行,减少了线程上下文切换的开销。
2)多线程是开发高并发系统的基础,利用好多线程机制可以大大提高系统整体的并发能力以及性能。
多线程可能带来的问题:并发编程的目的就是为了能提高程序的执行效率提高程序运行速度,但是并发编程并不总是能提高程序运行速度的,而且并发编程可能会遇到很多问题,比如:内存泄漏、死锁、线程不安全等等


线程的生命状态和周期:
new Thread()创建一个线程------>调用start()方法等待运行----->各种状态:阻塞状态,等待锁释放、等待状态,等待其他线程做出一些特定动作、等待超时状态,在指定的时间后自行返回------>执行结束
线程创建之后它将处于 NEW(新建) 状态,调用 start() 方法后开始运行,线程这时候处于 READY(可运行) 状态。可运行状态的线程获得了 CPU 时间片(timeslice)后就处于 RUNNING(运行) 状态。



线程死锁:
线程死锁描述的是一种情况:多个线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放。由于线程被无限期地阻塞,因此程序不可能正常终止。
案例:

public class DeadLockDemo {private static Object resource1 = new Object();//资源 1private static Object resource2 = new Object();//资源 2public static void main(String[] args) {new Thread(() -> {synchronized (resource1) {System.out.println(Thread.currentThread() + "get resource1");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread() + "waiting get resource2");synchronized (resource2) {System.out.println(Thread.currentThread() + "get resource2");}}}, "线程 1").start();new Thread(() -> {synchronized (resource2) {System.out.println(Thread.currentThread() + "get resource2");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread() + "waiting get resource1");synchronized (resource1) {System.out.println(Thread.currentThread() + "get resource1");}}}, "线程 2").start();}
}


代码中线程1通过 synchronized (resource1) 获得 resource1 的监视器锁,然后使用sleep进入了休眠;休眠时线程2通过synchronized (resource2)获获得了resource12的监视器锁,然后进入休眠;当两个线程都醒来时,都会企图请求对方资源,,然后这两个线程就会陷入互相等待的状态,这也就产生了死锁。



线程死锁四个必要条件:
1、互斥条件:该资源任意一个时刻只由一个线程占用。
2、请求与保持条件:一个线程因请求资源而阻塞时,对已获得的资源保持不放。
3、不剥夺条件:线程已获得的资源在未使用完之前不能被其他线程强行剥夺,只有自己使用完毕后才释放资源。
4、循环等待条件:若干线程之间形成一种头尾相接的循环等待资源关系。
避免线程死锁:
1、破坏请求与保持条件 :一次性申请所有的资源。
2、破坏不剥夺条件 :占用部分资源的线程进一步申请其他资源时,如果申请不到,可以主动释放它占有的资源。
3、破坏循环等待条件 :靠按序申请资源来预防。按某一顺序申请资源,释放资源则反序释放。破坏循环等待条件。


sleep()方法和write()方法对比
共同点:两者都可以暂停线程的执行
区别:
1、sleep()方法没有释放所,write()方法释放了锁
2、wait() 通常被用于线程间交互/通信,sleep()通常被用于暂停执行。
3、wait() 方法被调用后,线程不会自动苏醒,需要别的线程调用同一个对象上的 notify()(该方法用于唤醒休眠线程)或者 notifyAll() (该方法用于唤醒所有休眠线程)方法。sleep()方法执行完成后,线程会自动苏醒,或者也可以使用 wait(long timeout) 超时后线程会自动苏醒。
4、sleep() 是 Thread 类的静态本地方法,wait() 则是 Object 类的本地方法。


run()方法与start()方法:
new 一个 Thread后调用 start() 方法,线程就会进入就绪状态,当该线程分配到时间后就可以开始执行。start()方法中执行了线程的相应准备工作,然后自动执行run()方法内容,真正实现多线程工作;
直接运行run() 方法,就会把run() 方法当成main线程下的一个普通方法去执行,并不会在某个线程中执行它,所以这并不是多线程工作。
总结:调用start()方法可以启动线程并使线程进入就绪状态;执行run()方法的话不会以多线程的方式执行。


Java中volatile关键字 的意义是禁用CPU缓存,如果一个变量使用volatile修饰,那么就是指示编译器,这个变量时共享且不稳定的,每次使用都要到主存中进行读取;
volatile 关键字能保证数据的可见性,但不能保证数据的原子性。synchronized 关键字两者都能保证


== synchronized (锁)关键字== 用于解决多个线程之间访问资源的同步性,可以保证被它修饰的方法或者代码块在任意时刻只能有一个线程执行。
synchronized 关键字的三种使用方式:
1、修饰实例方法(锁当前对象实例)
给当前对象实例加锁,进入同步代码前要获得 当前对象实例的锁 。

synchronized void method() {//业务代码
}


2、修饰静态方法(锁当前类)
给当前类加锁,会作用于类的所有对象实例 ,进入同步代码前要获得 当前 class 的锁。这是因为静态成员不属于任何一个实例对象,归整个类所有,不依赖于类的特定实例,被类的所有实例共享。

synchronized static void method() {//业务代码
}


3、修饰代码块(锁指定对象/类)
对括号里指定的对象/类加锁:
1)synchronized(object) 表示进入同步代码库前要获得 给定对象的锁。
2)synchronized(类.class) 表示进入同步代码前要获得 给定 Class 的锁

synchronized(this) {//业务代码
}


总结:
1)synchronized 关键字加到 static 静态方法和 synchronized(class) 代码块上都是是给 Class 类上锁;
2)synchronized 关键字加到实例方法上是给对象实例上锁;
3)尽量不要使用 synchronized(String a) 因为 JVM 中,字符串常量池具有缓存功能。


synchronized 和 Lock(接口,ReentrantLock是实现类) 的区别
相同点:这两种锁都是可重入锁,可重入锁就是指当一个线程获取锁对象后,这个线程可以再次获取本对象上的锁,而其他线程是不可以的
不同点:
1、synchronized 是依赖于JVM实现的,它可以放在方法前面,也可以放在代码块前面(需要指定上锁对象),通常和wait(释放占有的对象锁,释放CPU)、notify(唤醒一个线程并获得锁进行访问)、notifyAll(唤醒所有线程,并采用竞争获得锁);Lock锁是API层面的,需要lock()(上锁方法)和unlock()(释放锁方法)配合try/finally语句来完成。
2、由于synchronized 是在JVM层面实现的,系统会监控锁的释放与否;而Lock锁是API层面对,需要手动释放锁(finally中)
3、Lock锁相比synchronized 增加了一些高级功能:(1)等待可中断,即正在等待的线程可以选择放弃等待,改为处理其他事情;可实现公平锁,通过实现类ReentrantLock中的ReentrantLock(boolean fair)构造方法来制定是否是公平的;可实现选择性通知(锁可以绑定多个条件),synchronized关键字与wait()和notify()/notifyAll()方法相结合可以实现等待/通知机制。ReentrantLock类当然也可以实现,但是需要借助于Condition接口与newCondition()方法。



27、Java8新特性


一、接口
在JDK1.8中针对接口的修改与现有的实现不兼容的问题,新interface的方法可以用default或static修饰,这样就可以有方法体,实现类也不必重写此方法。
1、default修饰的方法,是普通实例方法,可以用this调用,可以被子类继承、重写。
2、static修饰的方法,使用上和一般类静态方法一样。但它不能被子类继承,只能用Interface调用。
接口和抽象类的区别(重要):1、接口可以多继承(extends)其它接口,一个类可以实现(implement)多个接口。一个类只能单继承一个抽象类;接口中抽象方法默认修饰符public abstract,常量用public static final 修饰。抽象类可以使用其他修饰符;


二、函数式接口
函数式接口:有且仅有一个抽象方法的接口
Java中的函数式变成体现就是Lambda表达式,所以函数式接口就是可以适用于Lambda表达式的接口
函数式接口的判定:接口有@FunctionalInterface注解的都是函数式接口;


三、Lambda表达式
Lambda表达式允许把函数作为一个方法的参数传递进方法中。
Lambda表达式的使用前提:有一个接口,接口中有且只有一个抽象方法.
举几个例子:
Runnable接口:

//匿名内部类new Thread(new Runnable() {@Overridepublic void run() {System.out.println("多线程程序启动了");}}).start();//Lambda表达式
new Thread(()->{System.out.println("多线程程序启动了");
}).start();


Lambda表达式与匿名内部类区别:1、匿名内部类可以是接口、抽象类、具体类中的一种,Lambda只能是使用在接口上;2、使用限制上,当接口中有仅有一个抽象方法,两者都可以使用,当接口中有多个抽象方法只能使用匿名内部类。3、在实现原理上,匿名内部类的方式在编译后会产生单独的字节码文件;Lambda表达式则没有一个单独的字节码文件,其对应的字节会在运行时动态生成。


四、Date-Tome API
主要解决了之前Date API的几个不足:1、非线程安全;2、时区处理麻烦;3、各种格式化和时间计算繁琐;4设计有缺陷,Date 类同时包含日期和时间;还有一个 java.sql.Date,容易混淆


五、Stream流
Stream依然不存储数据,不同的是它可以检索(Retrieve)和逻辑处理集合数据、包括筛选、排序、统计、计数等。可以想象成是 Sql 语句。它的源数据可以是 Collection、Array 等。由于它的方法参数都是函数式接口类型,所以一般和 Lambda 配合使用。
如筛选张姓名字长度为3,并进行输出,先对array生成流,利用filter()过滤数据最后打印
除此之外还有:limit()方法限制输出个数;skip()跳过指定个数进行输出等等

array.stream().filter(s->s.startsWith("张")).
filter(s -> s.length()==3).forEach(s-> System.out.println(s));

Java八股整理--java基础相关推荐

  1. Java笔记整理(基础)

    软件:一系列按照特定顺序组织的计算及数据和指令的集合 系统软件   应用软件 应用程序=算法+数据结构 机器语言  汇编语言  高级语言 1996  SUN发布JDK1.0    C/S (c客户端) ...

  2. java 01 02_Java知识系统回顾整理01基础02面向对象01类和对象

    一.面向对象实例--设计英雄这个类 LOL有很多英雄,比如盲僧,团战可以输,提莫必须死,盖伦,琴女 所有这些英雄,都有一些共同的状态 比如,他们都有名字,hp,护甲,移动速度等等 这样我们就可以设计一 ...

  3. Java笔记整理-02.Java基础语法

    1,标识符 由英文字母.数字._(下划线)和$组成,长度不限.其中英文字母包含大写字母(A-Z)和小写字母(a-z),数字包含0到9. 标识符的第一个字符不能是数字(即标识符不能以数字开头). 标识符 ...

  4. Java知识整理——基础知识

    什么是JVM? 为什么称Java为跨平台的编程语言? Java虚拟机(Java Virtual Machine)是可以执行Java字节码的虚拟机,每个Java源文件将被编译成字节码文件,然后在JVM中 ...

  5. java基础项目_Java 教程整理:基础、项目全都有

    Java 在编程语言排行榜中一直位列前排,可知 Java 语言的受欢迎程度了. 网上有很多 Java 教程,无论是基础入门还是开发小项目的教程都比比皆是,可是系统的很少,对于Java 学习者来说找到系 ...

  6. 整理Java基础知识--Date Time2

    日期转换符的用法 import java.util.*; public class DateDemo{public static void main(String[] args){ Date date ...

  7. 面试之Java知识整理

    1.面向对象都有哪些特性 继承.封装.多态性.抽象 2.Java中实现多态的机制是什么? 继承与接口 3.Java中异常分为哪些种类 3.1按照异常需要处理的时机分为编译时异常(CheckedExce ...

  8. 零基础可以学习java吗_零基础真的可以学习java吗?

    Java是一个比较抽象的开发语言,涉及知识点比较多,如果自学的话,可以按照五个阶段来学习,先学好基础知识,再逐步扩展,由易到难.要注意视频和书本内容相辅相成,切记不要只看视频而不忽略书本基础的知识要点 ...

  9. 深入入门正则表达式(java) - 1 - 入门基础

    深入入门正则表达式(java) - 引言 深入入门正则表达式(java) - 1 - 入门基础  深入入门正则表达式(java) - 2 - 基本实例 深入入门正则表达式(java) - 3 - 正则 ...

最新文章

  1. 电子设计搜索引擎引入分析和见解
  2. Java工程师的成长路线图是什么?
  3. echarts词云图形状_怎么用Python画出好看的词云图?
  4. window mobile 防止系统休眠代码
  5. Redis中的哨兵机制的不足
  6. 大学编译原理试卷考试题
  7. jQuery kxbdMarquee 无缝滚动
  8. Python对文件的操作(转)
  9. 662. 二叉树最大宽度
  10. 基于维特比算法的概率路径
  11. HGOI20190707 题解
  12. 设计模式学习笔记——模板(Template)模式
  13. 2万月薪招聘名校学生养猪:不好好学习,养猪都不要你
  14. vSAN其实很简单-运维工程师眼里的vSAN
  15. wxPython下载安装教程
  16. english need study and insist 1
  17. Linux双网卡绑定bond0(单IP)
  18. macOS app动态修改app图标,图标为icns格式
  19. 这个AR/VR设计原型利器,爆赞
  20. hibernate之SessionFactory和Session

热门文章

  1. 解决fastjson在parse解析str时无序
  2. 为什么有些企业喜欢做背景调查呢
  3. python中if判断语句、while循环语句、for循环遍历、break 和continue作用
  4. SAP-FI模块 处理自动生成会计凭证增强
  5. 【ZZULIOJ】1102: 火车票退票费计算(函数专题)
  6. 物理模拟-更加真实的动态水面
  7. postgresql命令不生效解决办法
  8. 目前在台式计算机上,笔记本硬盘在台式机上能用吗【详细介绍】
  9. 按键精灵重新定义Print Screen按键
  10. 2006-10-06 引钗头凤两首送高旭明