面向对象的特征?

四点:封装、继承、多态、抽象。
封装
public:具有最大的访问权限,可以访问任何一个在classpath下的类、接口、异常等。它往往用于对外的情况,也就是对象或类对外的一种接口的形式。
protected:主要的作用就是用来保护子类的。它的含义在于子类可以用它修饰的成员,其他的不可以,它相当于传递给子类的一种继承的东西
default:有时候也称为friendly,它是针对本包访问而设计的,任何处于本包下的类、接口、异常等,都可以相互访问,即使是父类没有用protected修饰的成员也可以。
private:访问权限仅限于类的内部,是一种封装的体现,例如,大多数成员变量都是修饰符为private的,它们不希望被其他任何外部的类访问。

重载和重写的区别?

1)重写 override
方法名、参数、返回值相同。
子类方法不能缩小父类方法的访问权限。
子类方法不能抛出比父类方法更多的异常(但子类方法可以不抛出异常)。
存在于父类和子类之间。
方法被定义为 final 不能被重写。
2)重载 overload
参数类型、个数、顺序至少有一个不相同。
不能重载只有返回值不同的方法名。
存在于父类和子类、同类中。#

JDK、JRE、JVM 分别是什么关系?

JDK
JDK 即为 Java 开发工具包,包含编写 Java 程序所必须的编译、运行等开发工具以及 JRE。开发工具如:
用于编译 Java 程序的 javac 命令。
用于启动 JVM 运行 Java 程序的 Java 命令。
用于生成文档的 Javadoc 命令。
用于打包的 jar 命令等等。
简单说,就是 JDK 包含 JRE 包含 JVM。

JRE
JRE 即为 Java 运行环境,提供了运行 Java 应用程序所必须的软件环境,包含有 Java 虚拟机(JVM)和丰富的系统类库。系统类库即为 Java 提前封装好的功能类,只需拿来直接使用即可,可以大大的提高开发效率。
简单说,就是 JRE 包含 JVM。

JVM
JVM 即为 Java 虚拟机,提供了字节码文件(.class)的运行环境支持。

为什么 Java 被称作是“平台无关的编程语言”?

Java 虚拟机是一个可以执行 Java 字节码的虚拟机进程。
Java 源文件( .java )被编译成能被 Java 虚拟机执行的字节码文件( .class )。
Java 被设计成允许应用程序可以运行在任意的平台,而不需要程序员为每一个平台单独重写或者是重新编译。Java 虚拟机让这个变为可能,因为它知道底层硬件平台的指令长度和其他特性。
Java 源代码=> 编译器 => JVM 可执行的 Java 字节码(即虚拟指令) => JVM => JVM 中解释器 => 机器可执行的二进制机器码 => 程序运行

Java 中的几种基本数据类型是什么?各自占用多少字节?

Java 支持的数据类型包括基本数据类型和引用类型。
基本数据类型如下:
整数值型:byte、short、int、long
字符型:char
浮点类型:float、double
布尔型:boolean

整数型:默认 int 型,小数默认是 double 型。Float 和 Long 类型的必须加后缀。比如:float f = 100f 。

引用类型声明的变量是指该变量在内存中实际存储的是一个引用地址,实体在堆中。
引用类型包括类、接口、数组等。
特别注意,String 是引用类型不是基本类型。

什么是值传递和引用传递?

值传递,是对基本型变量而言的,传递的是该变量的一个副本,改变副本不影响原变量。
引用传递,一般是对于对象型变量而言的,传递的是该对象地址的一个副本,并不是原对象本身。
一般认为,Java 内的传递都是值传递,Java 中实例对象的传递是引用传递。

char 型变量中能不能存贮一个中文汉字?为什么?

在 C 语言中,char 类型占 1 个字节,而汉字占 2 个字节,所以不能存储。
在 Java 语言中,char 类型占 2 个字节,而且 Java 默认采用 Unicode 编码,一个 Unicode 码是 16 位,所以一个 Unicode 码占两个字节,Java 中无论汉字还是英文字母,都是用 Unicode 编码来表示的。所以,在 Java 中,char 类型变量可以存储一个中文汉字。

什么是自动拆装箱?

自动装箱和拆箱,就是基本类型和引用类型之间的转换。

为什么要转换?
如果你在 Java5 下进行过编程的话,你一定不会陌生这一点,你不能直接地向集合( Collection )中放入原始类型值,因为集合只接收对象。

  • 通常这种情况下你的做法是,将这些原始类型的值转换成对象,然后将这些转换的对象放入集合中。使用 Integer、Double、Boolean 等这些类,我们可以将原始类型值转换成对应的对象,但是从某些程度可能使得代码不是那么简洁精炼。
  • 为了让代码简练,Java5 引入了具有在原始类型和对象类型自动转换的装箱和拆箱机制。
  • 但是自动装箱和拆箱并非完美,在使用时需要有一些注意事项,如果没有搞明白自动装箱和拆箱,可能会引起难以察觉的 Bug 。

Object 通用方法

public native int hashCode()

public boolean equals(Object obj)

protected native Object clone() throws CloneNotSupportedException

public String toString()

public final native Class<?> getClass()

protected void finalize() throws Throwable {}

public final native void notify()

public final native void notifyAll()

public final native void wait(long timeout) throws InterruptedException

public final void wait(long timeout, int nanos) throws InterruptedException

public final void wait() throws InterruptedException

== 和 equals 的区别是什么?

值类型(int,char,long,boolean 等)的话

  • 都是用 == 判断相等性。
  • 没有equals方法

对象引用的话

  • == 判断引用所指的对象是否是同一个
  • equals 方法,是 Object 的成员函数,有些类会覆盖(override) 这个方法,用于判断对象的等价性

两个对象的 hashCode()相同,则 equals()也一定为 true,对吗?

equals 方法,用于比较对象的内容是否相等。

  • 当覆盖了 equals 方法时,比较对象是否相等将通过覆盖后的 equals 方法进行比较(判断对象的内容是否相等)。

hashCode 方法,大多在集合中用到。

  • 将对象放入到集合中时,首先判断要放入对象的 hashCode 值与集合中的任意一个元素的 hashCode 值是否相等,如果不相等直接将该对象放入集合中。
  • 如果 hashCode 值相等,然后再通过 equals 方法判断要放入对象与集合中的任意一个对象是否相等,如果 equals 判断不相等,直接将该元素放入到集合中,否则不放入。

当我们重写一个类的 equals 方法时就应当连同重写 hashcode 方法,并且两个方法应满足:
1:一致性,即:当两个对象 equals 比较为 true,那么 hashcode 值应当相等,反之亦然,因为当两个对象hashcode 值相等,但是 equals 比较为 false,那么在 HashMap 中会产生链表,影响查询性能。
2:成对重写,即重写 equals 就应当重写 hashcode。

java 中的 Math.round(11.5) 等于多少?

  • 首先要注意的是它的返回值类型是long,如果 Math.round(11.5f),那它的返回值类型就是int,这一点可以参考API
  • 其次 Returns the closest long to the argument, with ties rounding to positive infinity它返回的是一个最接近参数的long 值(例如:Math.round(11.6) = 12;Math.round(-11.6) = -12;Math.round(-0.1) = 0;Math.round(0.1) = 0),那如果出现向上向下距离一样的数值,比如题目中的11.5,该如何处理呢,别着急,看它的后半句话,with ties rounding to positive infinity(同时向正无穷方向取舍或者翻译成取较大的值,英语水平较差,只能翻译成这样了;
    例子: Math.round(11.5) ,首先与 11.5最接近的有两个整数 11 和 12,取较大的那结果就是12;
    Math.round(-11.5),首先与 -11.5最接近的有两个整数 -11 和 -12,取较大的那结果就是-11;
    Math.round(0.5),首先与 0.5最接近的有两个整数 0 和 1,取较大的那结果就是1;
    Math.round(-0.5),首先与 -0.5最接近的有两个整数 -1 和 0,取较大的那结果就是0;)
  • 然后它有三个特例:
    1.如果参数为 NaN(无穷与非数值),那么结果为 0。
    2.如果参数为负无穷大或任何小于等于 Long.MIN_VALUE 的值,那么结果等于Long.MIN_VALUE 的值。
    3.如果参数为正无穷大或任何大于等于 Long.MAX_VALUE 的值,那么结果等于Long.MAX_VALUE 的值。

String s = new String(“xyz”) 会创建几个对象?

首先,在 String 池内找,找到 “xyz” 字符串,不创建 “xyz” 对应的 String 对象,否则创建一个对象。
然后,遇到 new 关键字,在内存上创建 String 对象,并将其返回给 s ,又一个对象。
所以,总共是 1 个或者 2 个对象。

如何将字符串反转?

通过 charAt(int index)返回char值进行字符串拼接
调用StringBuffer中的reverse方法
把字符串转换成字符数组首位对调位置
把字符串转换成字符数组倒叙拼接然后返回值

String 类的常用方法都有那些?

1、求字符串长度
public int length()//返回该字符串的长度
2、求字符串某一位置字符
public char charAt(int index)//返回字符串中指定位置的字符;注意字符串中第一个字符索引是0,最后一个是length()-1。
3、提取子串
用String类的substring方法可以提取字符串中的子串,该方法有两种常用参数:
1)public String substring(int beginIndex)//该方法从beginIndex位置起,从当前字符串中取出剩余的字符作为一个新的字符串返回。
2)public String substring(int beginIndex, int endIndex)//该方法从beginIndex位置起,从当前字符串中取出到endIndex-1位置的字符作为一个新的字符串返回。
4、字符串比较
1)public int compareTo(String anotherString)//该方法是对字符串内容按字典顺序进行大小比较,通过返回的整数值指明当前字符串与参数字符串的大小关系。若当前对象比参数大则返回正整数,反之返回负整数,相等返回0。
2)public int compareToIgnore(String anotherString)//与compareTo方法相似,但忽略大小写。
3)public boolean equals(Object anotherObject)//比较当前字符串和参数字符串,在两个字符串相等的时候返回true,否则返回false。
4)public boolean equalsIgnoreCase(String anotherString)//与equals方法相似,但忽略大小写。
5、字符串连接
public String concat(String str)//将参数中的字符串str连接到当前字符串的后面,效果等价于"+"。
6、字符串中单个字符查找
1)public int indexOf(int ch/String str)//用于查找当前字符串中字符或子串,返回字符或子串在当前字符串中从左边起首次出现的位置,若没有出现则返回-1。
2)public int indexOf(int ch/String str, int fromIndex)//改方法与第一种类似,区别在于该方法从fromIndex位置向后查找。
3)public int lastIndexOf(int ch/String str)//该方法与第一种类似,区别在于该方法从字符串的末尾位置向前查找。
4)public int lastIndexOf(int ch/String str, int fromIndex)//该方法与第二种方法类似,区别于该方法从fromIndex位置向前查找。
7、字符串中字符的大小写转换
1)public String toLowerCase()//返回将当前字符串中所有字符转换成小写后的新串
2)public String toUpperCase()//返回将当前字符串中所有字符转换成大写后的新串
8、字符串中字符的替换
1)public String replace(char oldChar, char newChar)//用字符newChar替换当前字符串中所有的oldChar字符,并返回一个新的字符串。
2)public String replaceFirst(String regex, String replacement)//该方法用字符replacement的内容替换当前字符串中遇到的第一个和字符串regex相匹配的子串,应将新的字符串返回。
3)public String replaceAll(String regex, String replacement)//该方法用字符replacement的内容替换当前字符串中遇到的所有和字符串regex相匹配的子串,应将新的字符串返回。
9、其他类方法
1)String trim()//截去字符串两端的空格,但对于中间的空格不处理。
2)boolean startWith(String prefix)boolean endWith(String suffix)//用来比较当前字符串的起始字符或子字符串prefix和终止字符或子字符串suffix是否和当前字符串相同,重载方法中同时还可以指定比较的开始位置offset。
3)regionMatches(boolean b, int firstStart, String other, int otherStart, int length)//从当前字符串的firstStart位置开始比较,取长度为length的一个子字符串,other字符串从otherStart位置开始,指定另外一个长度为length的字符串,两字符串比较,当b为true时字符串不区分大小写。
4)contains(String str)//判断参数s是否被包含在字符串中,并返回一个布尔类型的值。
5)String[] split(String str)//将str作为分隔符进行字符串分解,分解后的字字符串在字符串数组中返回。
字符串与基本类型的转换
1、字符串转换为基本类型
java.lang包中有Byte、Short、Integer、Float、Double类的调用方法:
1)public static byte parseByte(String s)
2、基本类型转换为字符串类型
String类中提供了String valueOf()放法,用作基本类型转换为字符串类型。
1)static String valueOf(char data[])
3、进制转换
使用Long类中的方法得到整数之间的各种进制转换的方法:Long.toBinaryString(long l)

抽象类必须要有抽象方法吗?

抽象类是为了把相同的但不确定的东西的提取出来,为了以后的重用。定义成抽象类的目的,就是为了在子类中实现抽象类。
抽象方法不能有方法主体。
抽象类可以有构造函数,被继承时子类必须继承父类一个构造方法,抽象方法不能被声明为静态(JDK1.8 以后可以包含)。
抽象类不能被实例化。因为抽象类中方法未具体化,这是一种不完整的类,所以直接实例化也就没有意义了。
抽象类中不一定要包含abstrace方法。也就是了,抽象中可以没有abstract方法。
一旦类中包含了abstract方法,那类该类必须声明为abstract类。
抽象类不能使用 final 修饰

接口和抽象类有什么区别?

a. 抽象类可以有构造方法,接口中不能有构造方法。
b. 抽象类中可以有普通成员变量,接口中没有普通成员变量。
c. 抽象类中可以包含非抽象普通方法,接口中的所有方法必须都是抽象的,不能有非抽象的方法(1.8可以添加默认方法)。
d. 抽象类中的抽象方法的访问权限可以是 public、protected 和(默认类型,虽然 eclipse 不报错,但也不能用,默认类型子类不能继承),接口中的抽象方法只能是 public 类型的,并且默认即为 public abstract 类型。
e. 抽象类中可以包含静态方法,在 JDK1.8 之前接口中不能不包含静态方法,JDK1.8 以后可以包含。
f. 抽象类和接口中都可以包含静态成员变量,抽象类中的静态成员变量的访问权限可以是任意的,但接口中定义的变量只能是 public static final 类型的,并且默认即为 public static final 类型。
g. 一个类可以实现多个接口,用逗号隔开,但只能继承一个抽象类,接口不可以实现接口,但可以继承接口,并且可以继承多个接口,用逗号隔开。

继承和组合的区别在哪?

继承:指的是一个类(称为子类、子接口)继承另外的一个类(称为父类、父接口)的功能,并可以增加它自己的新功能的能力,继承是类与类或者接口与接口之间最常见的关系。在 Java 中,此类关系通过关键字 extends 明确标识,在设计时一般没有争议性。
组合:组合是关联关系的一种特例,他体现的是整体与部分、拥有的关系,即 has-a 的关系,此时整体与部分之间是可分离的,他们可以具有各自的生命周期,部分可以属于多个整体对象,也可以为多个整体对象共享。

接口中定义的变量只能是 public static final 类型的

  1. 首先接口是一种高度抽象的"模版",,而接口中的属性也就是’模版’的成员,就应当是所有实现"模版"的实现类的共有特性,所以它是public static的 ,是所有实现类共有的 .假如可以是非static的话,因一个类可以继承多个接口,出现重名的变量,就无法区分了,使用static就可以用接口区分
  2. 其次,接口中如果可能定义非final的变量的话,而方法又都是abstract的,这就自相矛盾了,有可变成员变量但对应的方法却无法操作这些变量,虽然可以直接修改这些静态成员变量的值,但所有实现类对应的值都被修改了,这跟抽象类有何区别?又接口是一种更高层面的抽象,是一种规范、功能定义的声明,所有可变的东西都应该归属到实现类中,这样接口才能起到标准化、规范化的作用。所以接口中的属性必然是final的。
  3. 最后,接口只是对事物的属性和行为更高层次的抽象 。对修改关闭,对扩展(不同的实现implements)开放,接口是对开闭原则(Open-Closed Principle )的一种体现。

final

  1. 数据
    声明数据为常量,可以是编译时常量,也可以是在运行时被初始化后不能被改变的常量。
    对于基本类型,final 使数值不变;
    对于引用类型,final 使引用不变,也就不能引用其它对象,但是被引用的对象本身是可以修改的。

  2. 方法
    声明方法不能被子类重写。
    private 方法隐式地被指定为 final,如果在子类中定义的方法和基类中的一个 private 方法签名相同,此时子类的方法不是重写基类方法,而是在子类中定义了一个新的方法。


  3. 声明类不允许被继承。

static

  1. 静态变量
  • 静态变量:又称为类变量,也就是说这个变量属于类的,类所有的实例都共享静态变量,可以直接通过类名来访问它。静态变量在内存中只存在一份。
  • 实例变量:每创建一个实例就会产生一个实例变量,它与该实例同生共死。
  1. 静态方法
    静态方法在类加载的时候就存在了,它不依赖于任何实例。所以静态方法必须有实现,也就是说它不能是抽象方法。
    只能访问所属类的静态字段和静态方法,方法中不能有 this 和 super 关键字。
  2. 静态语句块
    静态语句块在类初始化时运行一次。
  3. 静态内部类
    非静态内部类依赖于外部类的实例,而静态内部类不需要。
    静态内部类不能访问外部类的非静态的变量和方法。

讲讲类的实例化顺序?

静态变量和静态语句块优先于实例变量和普通语句块,静态变量和静态语句块的初始化顺序取决于它们在代码中的顺序。
存在继承的情况下,初始化顺序为:

  • 父类(静态变量、静态语句块)
  • 子类(静态变量、静态语句块)
  • 父类(实例变量、普通语句块)
  • 父类(构造函数)
  • 子类(实例变量、普通语句块)
  • 子类(构造函数)

什么是内部类?

《Java 内部类总结(吐血之作)》 文章。
???? 内部类的作用是什么?
内部类提供了更好的封装,除了该外围类,其他类都不能访问。
???? Anonymous Inner Class(匿名内部类)是否可以继承其它类?是否可以实现接口?
可以继承其他类或实现其他接口,在 Java 集合的流式操作中,我们常常这么干。
???? 内部类可以引用它的包含类(外部类)的成员吗?有没有什么限制?
一个内部类对象可以访问创建它的外部类对象的成员,包括私有成员。

java 中 IO 流分为几种?

面试||java基础
1.Java IO是采用的是装饰模式,即采用处理流来包装节点流的方式,来达到代码通用性。
2.处理流和节点流的区分方法,节点流在新建时需要一个数据源(文件、网络)作为参数,而处理流需要一个节点流作为参数。
3.处理流的作用就是提高代码通用性,编写代码的便捷性,提高性能。
4.节点流都是对应抽象基类的实现类,它们都实现了抽象基类的基础读写方法。其中read()方法如果返回-1,代表已经读到数据源末尾。

BIO、NIO、AIO 有什么区别?

IO模型主要分类:

  • 同步(synchronous) IO和异步(asynchronous) IO
  • 阻塞(blocking) IO和非阻塞(non-blocking)IO
  • 同步阻塞(blocking-IO)简称BIO
  • 同步非阻塞(non-blocking-IO)简称NIO
  • 异步非阻塞(synchronous-non-blocking-IO)简称AIO
  1. BIO (同步阻塞I/O模式)
    数据的读取写入必须阻塞在一个线程内等待其完成。
    这里使用那个经典的烧开水例子,这里假设一个烧开水的场景,有一排水壶在烧开水,BIO的工作模式就是, 叫一个线程停留在一个水壶那,直到这个水壶烧开,才去处理下一个水壶。但是实际上线程在等待水壶烧开的时间段什么都没有做。

  2. NIO(同步非阻塞)
    同时支持阻塞与非阻塞模式,但这里我们以其同步非阻塞I/O模式来说明,那么什么叫做同步非阻塞?如果还拿烧开水来说,NIO的做法是叫一个线程不断的轮询每个水壶的状态,看看是否有水壶的状态发生了改变,从而进行下一步的操作。
    NIO的最重要的地方是当一个连接创建后,不需要对应一个线程,这个连接会被注册到多路复用器上面,所以所有的连接只需要一个线程就可以搞定,当这个线程中的多路复用器进行轮询的时候,发现连接上有请求的话,才开启一个工作线程进行处理,也就是一个请求一个线程模式。

  3. AIO (异步非阻塞I/O模型)
    异步非阻塞与同步非阻塞的区别在哪里?异步非阻塞无需一个线程去轮询所有IO操作的状态改变,在相应的状态改变后,系统会通知对应的线程来处理。对应到烧开水中就是,为每个水壶上面装了一个开关,水烧开之后,水壶会自动通知我水烧开了。

  4. IO与NIO区别:
    面试||java基础
    BIO是一个连接一个线程。
    NIO是一个请求一个线程。
    AIO是一个有效请求一个线程。

  5. 同步与异步的区别:
    同步:发送一个请求,等待返回,再发送下一个请求,同步可以避免出现死锁,脏读的发生。
    异步:发送一个请求,不等待返回,随时可以再发送下一个请求,可以提高效率,保证并发。

  6. 阻塞和非阻塞
    阻塞:传统的IO流都是阻塞式的。也就是说,当一个线程调用read()或者write()方法时,该线程将被阻塞,直到有一些数据读读取或者被写入,在此期间,该线程不能执行其他任何任务。在完成网络通信进行IO操作时,由于线程会阻塞,所以服务器端必须为每个客户端都提供一个独立的线程进行处理,当服务器端需要处理大量的客户端时,性能急剧下降。
    非阻塞:Java
    NIO是非阻塞式的。当线程从某通道进行读写数据时,若没有数据可用时,该线程会去执行其他任务。线程通常将非阻塞IO的空闲时间用于在其他通道上执行IO操作,所以单独的线程可以管理多个输入和输出通道。因此NIO可以让服务器端使用一个或有限几个线程来同时处理连接到服务器端的所有客户端。

  7. BIO、NIO、AIO适用场景
    BIO方式适用于连接数目比较小且固定的架构,这种方式对服务器资源要求比较高,并发局限于应用中,JDK1.4以前的唯一选择。
    NIO方式适用于**连接数目多且连接比较短(轻操作)的架构,比如聊天服务器,并发局限于应用中,编程比较复杂。
    AIO方式使用于
    连接数目多且连接比较长(重操作)**的架构,比如相册服务器,充分调用OS参与并发操作,编程比较复杂,JDK7开始支持。

NIO的3个核心概念

NIO重点是把Channel(通道)Buffer(缓冲区)Selector(选择器) 三个类之间的关系弄清楚。

  1. 缓冲区Buffer
    Buffer是一个对象。它包含一些要写入或者读出的数据。在面向流的I/O中,可以将数据写入或者将数据直接读到Stream对象中。
    在NIO中,所有的数据都是用缓冲区处理。这也就本文上面谈到的IO是面向流的,NIO是面向缓冲区的。
    缓冲区实质是一个数组,通常它是一个字节数组(ByteBuffer),也可以使用其他类的数组。但是一个缓冲区不仅仅是一个数组,缓冲区提供了对数据的结构化访问以及维护读写位置(limit)等信息。
    最常用的缓冲区是ByteBuffer,一个ByteBuffer提供了一组功能于操作byte数组。除了ByteBuffer,还有其他的一些缓冲区,事实上,每一种Java基本类型(除了Boolean)都对应一种缓冲区,具体如下:
  • ByteBuffer:字节缓冲区
  • CharBuffer:字符缓冲区
  • ShortBuffer:短整型缓冲区
  • IntBuffer:整型缓冲区
  • LongBuffer:长整型缓冲区
  • FloatBuffer:浮点型缓冲区
  • DoubleBuffer:双精度浮点型缓冲区
  1. 通道Channel
    Channel是一个通道,可以通过它读取和写入数据,他就像自来水管一样,网络数据通过Channel读取和写入。
    通道和流不同之处在于通道是双向的,流只是在一个方向移动,而且通道可以用于读,写或者同时用于读写。
    因为Channel是全双工的,所以它比流更好地映射底层操作系统的API,特别是在UNIX网络编程中,底层操作系统的通道都是全双工的,同时支持读和写。
    Channel有四种实现:
  • FileChannel:是从文件中读取数据。
  • DatagramChannel:从UDP网络中读取或者写入数据。
  • SocketChannel:从TCP网络中读取或者写入数据。
  • ServerSocketChannel:允许你监听来自TCP的连接,就像服务器一样。每一个连接都会有一个SocketChannel产生。
  1. 多路复用器Selector
    Selector选择器可以监听多个Channel通道感兴趣的事情(read、write、accept(服务端接收)、connect,实现一个线程管理多个Channel,节省线程切换上下文的资源消耗。Selector只能管理非阻塞的通道,FileChannel是阻塞的,无法管理。
    关键对象
    1- Selector:选择器对象,通道注册、通道监听对象和Selector相关。
    2- SelectorKey:通道监听关键字,通过它来监听通道状态。
    监听注册
    1- 监听注册在Selector:socketChannel.register(selector, SelectionKey.OP_READ);
    监听的事件
    1- OP_ACCEPT: 接收就绪,serviceSocketChannel使用的
    2- OP_READ: 读取就绪,socketChannel使用
    3- OP_WRITE: 写入就绪,socketChannel使用
    4- OP_CONNECT: 连接就绪,socketChannel使用

什么是 Java 序列化?

序列化就是一种用来处理对象流的机制,所谓对象流也就是将对象的内容进行流化。

  • 可以对流化后的对象进行读写操作,也可将流化后的对象传输于网络之间
  • 序列化是为了解决在对对象流进行读写操作时所引发的问题。

反序列化的过程,则是和序列化相反的过程。

另外,我们不能将序列化局限在 Java 对象转换成二进制数组,例如说,我们将一个 Java 对象,转换成 JSON 字符串,或者 XML 字符串,这也可以理解为是序列化。

如何实现 Java 序列化?

如下的方式,就是 Java 内置的序列化方案,实际场景下,我们可以自定义序列化的方案,例如说 Google Protobuf 。
将需要被序列化的类,实现 Serializable 接口,该接口没有需要实现的方法,implements Serializable 只是为了标注该对象是可被序列化的。

  • 序列化
    • 然后,使用一个输出流(如:FileOutputStream)来构造一个 ObjectOutputStream(对象流)对象
    • 接着,使用 ObjectOutputStream 对象的 #writeObject(Object obj) 方法,就可以将参数为 obj 的对象写出(即保存其状态)。
  • 反序列化
    • 要恢复的话则用输入流。

Java 序列话中,如果有些字段不想进行序列化怎么办?

对于不想进行序列化的变量,使用 transient 关键字修饰。
当对象被序列化时,阻止实例中那些用此关键字修饰的的变量序列化。
当对象被反序列化时,被 transient 修饰的变量值不会被持久化和恢复。
transient 只能修饰变量,不能修饰类和方法。

error 和 exception 有什么区别?

Exception和Error都是继承于Throwable 类,在 Java 中只有 Throwable 类型的实例才可以被抛出(throw)或者捕获(catch),它是异常处理机制的基本组成类型。

  • Error是java程序运行中不可预料的异常情况,这种异常发生以后,会直接导致JVM不可处理或者不可恢复的情况。
    • 例如:内存资源不足等。
    • 对于这种错误,程序基本无能为力,除了退出运行外别无选择,它是由 Java 虚拟机抛出的。
  • Exception是java程序运行中可预料的异常情况,咱们可以获取到这种异常,并且对这种异常进行业务外的处理。Exception又分为检查性异常非检查性异常
    • 检查性异常Checked exception: 必须在编写代码时,使用try catch捕获(比如:IOException异常)。这类异常都是Exception的子类 。异常的向上抛出机制进行处理,假如子类可能产生A异常,那么在父类中也必须throws A异常。可能导致的问题:代码效率低,耦合度过高。
    • 非检查性异常 RuntimeException:在代码编写使,可以忽略捕获操作(比如:ArrayIndexOutOfBoundsException),这种异常是在代码编写或者使用过程中通过规范可以避免发生的。 切记,Error是Throw不是Exception 。这类异常都是RuntimeException的子类,虽然RuntimeException同样也是Exception的子类,但是它们是非凡的,它们不能通过client code来试图解决,所以称为Unchecked exception 。
      面试||java基础
      请列出 5 个运行时异常?
      NullPointerException
      IndexOutOfBoundsException
      ClassCastException
      ArrayStoreException
      BufferOverflowException

异常的使用的注意地方?
不要将异常处理用于正常的控制流(设计良好的 API 不应该强迫它的调用者为了正常的控制流而使用异常)。
对可以恢复的情况使用受检异常,对编程错误使用运行时异常。
避免不必要的使用受检异常(可以通过一些状态检测手段来避免异常的发生)。
优先使用标准的异常。
每个方法抛出的异常都要有文档。
保持异常的原子性
不要在 catch 中忽略掉捕获到的异常。

throw 与 throws 的区别 ?
throw ,用于在程序中显式地抛出一个异常。
throws ,用于指出在该方法中没有处理的异常。每个方法必须显式指明哪些异常没有处理,以便该方法的调用者可以预防可能发生的异常。最后,多个异常用逗号分隔。

异常处理中 finally 语句块的重要性?
不管程序是否发生了异常, finally 语句块都会被执行,甚至当没有catch 声明但抛出了一个异常时, finally 语句块也会被执行。
finally 语句块在 try 或者 catch 中的 return 语句之前执行的

finally 语句块通常用于释放资源, 如 I/O 缓冲区, 数据库连接等等。

  • 调用 System.exit(0) 这个方法,那么 finally 语句块不会执行
  • 当一个线程在执行 try 语句块或者 catch 语句块时被打断(interrupted)或者被终止(killed),与其相对应的 finally 语句块可能不会执行。
  • 还有更极端的情况,就是在线程运行 try 语句块或者 catch 语句块时,突然死机或者断电,finally 语句块肯定不会执行了。

说说反射的用途及实现

《什么是反射、反射可以做些什么》
每个类都有一个 Class 对象,包含了与类有关的信息。当编译一个新类时,会产生一个同名的 .class 文件,该文件内容保存着 Class 对象。
所谓反射其实是获取类的字节码文件,也就是.class文件,那么我们就可以通过Class这个对象进行获取。

获取Class对象的三种方式

  • Object ——> getClass();
  • 任何数据类型(包括基本数据类型)都有一个“静态”的class属性
  • 通过Class类的静态方法:forName(String className)(常用)

三种方式常用第三种,第一种对象都有了还要反射干什么。第二种需要导入类的包,依赖太强,不导包就抛编译错误。一般都第三种,一个字符串可以传入也可写在配置文件中等多种方法。

通过反射获取类的构造方法

1.获取构造方法:
1).批量的方法:
public Constructor[] getConstructors():所有”公有的”构造方法
public Constructor[] getDeclaredConstructors():获取所有的构造方法(包括私有、受保护、默认、公有)

2).获取单个的方法,并调用:
public Constructor getConstructor(Class… parameterTypes):获取单个的”公有的”构造方法:
public Constructor getDeclaredConstructor(Class… parameterTypes):获取”某个构造方法”可以是私有的,或受保护、默认、公有;

调用构造方法:
Constructor–>newInstance(Object… initargs)

2、newInstance是 Constructor类的方法(管理构造函数的类)
api的解释为:
newInstance(Object… initargs)
使用此 Constructor 对象表示的构造方法来创建该构造方法的声明类的新实例,并用指定的初始化参数初始化该实例。
它的返回值是T类型,所以newInstance是创建了一个构造方法的声明类的新实例对象。并为之调用

获取成员变量并调用:

1.批量的
1).Field[] getFields():获取所有的”公有字段”
2).Field[] getDeclaredFields():获取所有字段,包括:私有、受保护、默认、公有;
2.获取单个的:
1).public Field getField(String fieldName):获取某个”公有的”字段;
2).public Field getDeclaredField(String fieldName):获取某个字段(可以是私有的)

设置字段的值:
Field –> public void set(Object obj,Object value):
参数说明:
1.obj:要设置的字段所在的对象;
2.value:要为字段设置的值;

Java 反射机制主要提供了以下功能:

  • 在运行时构造一个类的对象。
  • 判断一个类所具有的成员变量和方法。
  • 调用一个对象的方法。
  • 生成动态代理。

反射的应用很多,很多框架都有用到:

  • Spring 框架的 IoC 基于反射创建对象和设置依赖属性。
  • Spring MVC 的请求调用对应方法,也是通过反射。
  • JDBC 的 Class#forName(String className) 方法,也是使用反射。

反射中,Class.forName 和 ClassLoader 区别

这两者,都可用来对类进行加载。差别在于:

  • Class#forName(…) 方法,除了将类的 .class 文件加载到JVM 中之外,还会对类进行解释,执行类中的 static 块。
  • ClassLoader 只干一件事情,就是将 .class 文件加载到 JVM 中,不会执行 static 中的内容,只有在 newInstance 才会去执行 static 块。

Class#forName(name, initialize, loader) 方法,带参函数也可控制是否加载 static 块,并且只有调用了newInstance 方法采用调用构造函数,创建类的对象。

什么是注解

Java 对象创建的方式?

使用 new 关键字创建对象。
使用 Class 类的 newInstance 方法(反射机制)。
使用 Constructor 类的 newInstance 方法(反射机制)。
使用 clone 方法创建对象。
使用(反)序列化机制创建对象。

Files的常用方法都有哪些?

Files.exists() 检测文件路径是否存在
Files.createFile()创建文件
Files.cteateDirectory()创建文件夹
Files.delete() 删除文件或者目录
Files.copy() 复制文件
Files.move() 移动文件
Files.size()查看文件个数
Files.read() 读取文件
Files.write()写入文件

相关文章: