一、java基础

1.重载和重写的区别

重载: 发生在同一个类中,方法名必须相同,参数类型不同、个数不同、顺序不同,方法返回值和访问修饰符可以 不同,发生在编译时。

重写: 发生在父子类中,方法名、参数列表必须相同,返回值范围小于等于父类,抛出的异常范围小于等于父类, 访问修饰符范围大于等于父类;如果父类方法访问修饰符为 private 则子类就不能重写该方法、

2.String 和 StringBuffer、StringBuilder 的区别是什么?String 为什么是不可变的?

可变性

简单的来说:String 类中使用 final 关键字字符数组保存字符串, private final char value[] ,所以 String 对象是不可变的。而StringBuilder 与 StringBuffer 都继承自 AbstractStringBuilder 类,在 AbstractStringBuilder 中也是使用字符数组保存字符串 char[]value 但是没有用 final 关键字修饰,所以这两种对象都是可变的。 StringBuilder 与 StringBuffer 的构造方法都是调用父类构造方法也就是 AbstractStringBuilder 实现的,大家可以自 行查阅源码。

AbstractStringBuilder.java

abstract class AbstractStringBuilder implements Appendable, CharSequence {
char[] value;
int count;
AbstractStringBuilder() {
}
AbstractStringBuilder(int capacity) {
value = new char[capacity];
}

线程安全性

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

性能

abstract class AbstractStringBuilder implements Appendable, CharSequence { char[] value; int count; AbstractStringBuilder() { } AbstractStringBuilder(int capacity) { value = new char[capacity]; } 每次对 String 类型进行改变的时候,都会生成一个新的 String 对象,然后将指针指向新的 String 对象。 StringBuffer 每次都会对 StringBuffer 对象本身进行操作,而不是生成新的对象并改变对象引用。相同情况下使用 StirngBuilder 相比使用 StringBuffer 仅能获得 10%~15% 左右的性能提升,但却要冒多线程不安全的风险。

对于三者使用的总结: 1. 操作少量的数据 = String 2. 单线程操作字符串缓冲区下操作大量数据 = StringBuilder 3. 多线程操作字符串缓冲区下操作大量数据 = StringBuffer

3.自动装箱和自动拆箱

装箱:将基本类型用它们对应的引用类型包装起来; 拆箱:将包装类型转换为基本数据类型;

4.“==”与equals

== : 它的作用是判断两个对象的地址是不是相等。即,判断两个对象是不是同一个对象。(基本数据类型==比较的是 值,引用数据类型==比较的是内存地址) equals() : 它的作用也是判断两个对象是否相等。但它一般有两种使用情况: 情况1:类没有覆盖 equals() 方法。则通过 equals() 比较该类的两个对象时,等价于通过“==”比较这两个对象。 情况2:类覆盖了 equals() 方法。一般,我们都覆盖 equals() 方法来两个对象的内容相等;若它们的内容相 等,则返回 true (即,认为这两个对象相等)。

举个例子:

说明: String 中的 equals 方法是被重写过的,因为 object 的 equals 方法是比较的对象的内存地址,而 String 的 equals 方法比较的是对象的值。 当创建 String 类型的对象时,虚拟机会在常量池中查找有没有已经存在的值和要创建的值相同的对象,如果有 就把它赋给当前引用。如果没有就在常量池中重新创建一个 String 对象。

5.关于final关键字

final关键字主要用在三个地方:变量、方法、类。

1. 对于一个final变量,如果是基本数据类型的变量,则其数值一旦在初始化之后便不能更改;如果是引用类型的 变量,则在对其初始化之后便不能再让其指向另一个对象。

2. 当用final修饰一个类时,表明这个类不能被继承。final类中的所有成员方法都会被隐式地指定为final方法。

3. 使用final方法的原因有两个。第一个原因是把方法锁定,以防任何继承类修改它的含义;第二个原因是效率。 在早期的Java实现版本中,会将final方法转为内嵌调用。但是如果方法过于庞大,可能看不到内嵌调用带来的 任何性能提升(现在的Java版本已经不需要使用final方法进行这些优化了)。类中所有的private方法都隐式地 指定为fianl。

描述 final、finally、finalize 的区别

答: final 修饰符(关键字)如果一个类被声明为 final,意味着它不能再派生出新的子类, 不能作为父类被继承。将变量或方法声明为 final,可以保证它们在使用中不被改变。被声 明为 final 的变量必须在声明时给定初值,而在以后的引用中只能读取,不可修改。被声 明为 final 的方法也同样只能使用,不能重载。

finally 在异常处理时提供 finally 块来执行任何清除操作。如果有 finally 的话,则不 管是否发生异常,finally 语句都会被执行。

finalize 方法名。Java 技术允许使用 finalize() 方法在垃圾收集器将对象从内存中清 除出去之前做必要清理工作。finalize() 方法是在垃圾收集器删除对象之前被调用的。它是 在 Object 类中定义的,因此所有的类都继承了它。子类覆盖 finalize() 方法以整理系统资 源或者执行其他清理工作。

6.Object类方法总结

Object类是一个特殊的类,是所有类的父类。它主要提供了以下11个方法:

7.java中的异常处理

在 Java 中,所有的异常都有一个共同的祖先java.lang包中的 Throwable类。Throwable: 有两个重要的子类: Exception(异常) 和 Error(错误) ,二者都是 Java 异常处理的重要子类,各自都包含大量子类。

Error(错误):是程序无法处理的错误,表示运行应用程序中较严重问题。大多数错误与代码编写者执行的操作无 关,而表示代码运行时 JVM(Java 虚拟机)出现的问题。例如,Java虚拟机运行错误(Virtual MachineError),当 JVM 不再有继续执行操作所需的内存资源时,将出现 OutOfMemoryError。这些异常发生时,Java虚拟机(JVM)一 般会选择线程终止。 这些错误表示故障发生于虚拟机自身、或者发生在虚拟机试图执行应用时,如Java虚拟机运行错误(Virtual MachineError)、类定义错误(NoClassDefFoundError)等。这些错误是不可查的,因为它们在应用程序的控制和 处理能力之 外,而且绝大多数是程序运行时不允许出现的状况。对于设计合理的应用程序来说,即使确实发生了错 误,本质上也不应该试图去处理它所引起的异常状况。在 Java中,错误通过Error的子类描述。

Exception(异常):是程序本身可以处理的异常。Exception 又 有 两 个 分 支 , 一 个 是 运 行 时 异 常 RuntimeException , 一 个 是 CheckedException。Exception 类有一个重要的子类 RuntimeException。 RuntimeException 异常由Java虚拟机抛出。NullPointerException(要访问的变量没有引用任何对象时,抛出该 异常)、ArithmeticException(算术运算异常,一个整数除以0时,抛出该异常)和 ArrayIndexOutOfBoundsException (下标越界异常)。检查异常 CheckedException:一般是外部错误,这种异常都发生在编译阶段,Java 编译器会强 制程序去捕获此类异常,即会出现要求你把这段可能出现异常的程序进行 try catch,该类异常一 般包括几个方面: 1. 试图在文件尾部读取数据 2. 试图打开一个错误格式的 URL 3. 试图根据给定的字符串查找 class 对象,而这个字符串表示的类并不存在

注意:异常和错误的区别:异常能被程序本身可以处理,错误是无法处理。

Throwable类常用方法 public string getMessage():返回异常发生时的详细信息 public string toString():返回异常发生时的简要描述 public string getLocalizedMessage():返回异常对象的本地化信息。使用Throwable的子类覆盖这个方法,可 以声称本地化信息。如果子类没有覆盖该方法,则该方法返回的信息与getMessage()返回的结果相同 public void printStackTrace():在控制台上打印Throwable对象封装的异常信息 。

异常处理总结 try 块:用于捕获异常。其后可接零个或多个catch块,如果没有catch块,则必须跟一个finally块。 catch 块:用于处理try捕获到的异常。 finally 块:无论是否捕获或处理异常,finally块里的语句都会被执行。当在try块或catch块中遇到return语句 时,finally语句块将在方法返回之前被执行。

在以下4种特殊情况下,finally块不会被执行: 1. 在finally语句块中发生了异常。 2. 在前面的代码中用了System.exit()退出程序。 3. 程序所在的线程死亡。 4. 关闭CPU。

异常处理方法:1.遇到问题不进行具体处理,而是继续抛给调用者 2.抛出异常

Throw 和 throws 的区别:

位置不同

1. throws 用在函数上,后面跟的是异常类,可以跟多个;而 throw 用在函数内,后面跟的 是异常对象。

功能不同:

2. throws 用来声明异常,让调用者只知道该功能可能出现的问题,可以给出预先的处理方 式;throw 抛出具体的问题对象,执行到 throw,功能就已经结束了,跳转到调用者,并 将具体的问题对象抛给调用者。也就是说 throw 语句独立存在时,下面不要定义其他语 句,因为执行不到。

3. throws 表示出现异常的一种可能性,并不一定会发生这些异常;throw 则是抛出了异常, 执行 throw 则一定抛出了某种异常对象。

4. 两者都是消极处理异常的方式,只是抛出或者可能抛出异常,但是不会由函数去处理异 常,真正的处理异常由函数的上层调用处理。

8. 获取用键盘输入常用的的两种方法

方法1:通过 Scanner

方法2:通过 BufferedReader

9.接口和抽象类的区别是什么

1. 接口的方法所有方法在接口中不能有实现(Java 8 开始接口方法可以有默认实现),抽象类可以有非抽象的方法。

2. 接口中的实例变量默认是 final 类型的,而抽象类中则不一定。

3. 一个类可以实现多个接口,但最多只能实现一个抽象类。

4. 一个类实现接口的话要实现接口的所有方法,而抽象类不一定。

5. 从设计层面来说,抽象是对类的抽象,是一种模板设计,接口是行为的抽象,是一种行为的规范。备注:在JDK8中,接口也可以定义静态方法,可以直接用接口名调用。实现类和实现是不可以调用的。如果同时实现 两个接口,接口中定义了一样的默认方法,必须重写,不然会报错。(详见 issue:https://github.com/Snailclimb/JavaGuide/issues/146)。

10.JAVA 反射

(1)反射概念(2)反射应用场景(3)反射使用步骤 (4)获取class对象的方法(5)创建对象的几种方法

10.1 动态语言

动态语言,是指程序在运行时可以改变其结构:新的函数可以引进,已有的函数可以被删除等结构上的变化。比如常见的 JavaScript 就是动态语言,除此之外 Ruby,Python 等也属于动态语言, 而 C、C++则不属于动态语言。从反射角度说 JAVA 属于半动态语言。

10.2 反射机制概念(运行状态中知道类所有的属性和方法)

在 Java 中的反射机制是指在运行状态中,对于任意一个类都能够知道这个类所有的属性和方法; 并且对于任意一个对象,都能够调用它的任意一个方法;这种动态获取信息以及动态调用对象方法的功能成为 Java 语言的反射机制。通过反射可以使程序代码访问装载到 JVM 中的类的内部信息 1) 获取已装载类的属性信息 2) 获取已装载类的方法 3) 获取已装载类的构造方法信息

10.3 反射的应用场景

编译时类型和运行时类型

在 Java 程序中许多对象在运行是都会出现两种类型:编译时类型和运行时类型。 编译时的类型由声明对象时实用的类型来决定,运行时的类型由实际赋值给对象的类型决定 。

如: Person p=new Student();

其中编译时类型为 Person,运行时类型为 Student。

程序在运行时还可能接收到外部传入的对象,该对象的编译时类型为 Object,但是程序有需要调用该对象的运行时类型的方法。为了解决这些问题,程序需要在运行时发现对象和类的真实信息。 然而,如果编译时根本无法预知该对象和类属于哪些类,程序只能依靠运行时信息来发现该对象和类的真实信息,此时就必须使用到反射了。

10.4 反射使用步骤(获取Class对象,调用对象方法)

1. 获取想要操作的类的 Class 对象,他是反射的核心,通过 Class 对象我们可以任意调用类的方 法。

2. 调用 Class 类中的方法,既就是反射的使用阶段。

3. 使用反射 API 来操作这些信息。

10.5 java反射API

反射API用来生成JVM中的类、接口或对象的信息

1. Class 类:反射的核心类,可以获取类的属性,方法等信息。

2. Field 类:Java.lang.reflec 包中的类,表示类的成员变量,可以用来获取和设置类之中的属性值。

3. Method 类: Java.lang.reflec 包中的类,表示类的方法,它可以用来获取类中的方法信息或者执行方法。

4. Constructor 类: Java.lang.reflec 包中的类,表示类的构造方法。

10.6 获取Class对象的3种方法

调用某个对象的 getClass()方法 Person p=new Person(); Class clazz=p.getClass();

调用某个类的 class 属性来获取该类对应的 Class 对象 Class clazz=Person.class;

使用 Class 类中的 forName()静态方法(最安全/性能最好) Class clazz=Class.forName("类的全路径"); (最常用)

当我们获得了想要操作的类的 Class 对象后,可以通过 Class 类中的方法获取并查看该类中的方法 和属性。

//获取 Person 类的 Class 对象
 Class clazz=Class.forName("reflection.Person");
//获取 Person 类的所有方法信息
Method[] method=clazz.getDeclaredMethods();
for(Method m:method){
     System.out.println(m.toString());
 }
 //获取 Person 类的所有成员属性信息
 Field[] field=clazz.getDeclaredFields();
 for(Field f:field){
     System.out.println(f.toString());
 }
 //获取 Person 类的所有构造方法信息
 Constructor[] constructor=clazz.getDeclaredConstructors();
 for(Constructor c:constructor){
     System.out.println(c.toString());
 }

10.7创建对象的两种方法

1. 使用 Class 对象的 newInstance()方法来创建该 Class 对象对应类的实例,但是这种方法要求 该 Class 对象对应的类有默认的空构造器。 调用 Constructor 对象的 newInstance()

2. 先使用 Class 对象获取指定的 Constructor 对象,再调用 Constructor 对象的 newInstance() 方法来创建 Class 对象对应类的实例,通过这种方法可以选定构造方法创建实例。

//获取 Person 类的 Class 对象
 Class clazz=Class.forName("reflection.Person");
 //使用.newInstane 方法创建对象
 Person p=(Person) clazz.newInstance();
//获取构造方法并创建对象
 Constructor c=clazz.getDeclaredConstructor(String.class,String.class,int.class);
 //创建对象并设置属性
 Person p1=(Person) c.newInstance("李四","男",20);

 不通过构造函数也能创建对象吗?

答:Java 创建对象的几种方式(重要):

1、 用 new 语句创建对象,这是最常见的创建对象的方法。

2 、 运 用 反 射 手 段 , 调 用 java.lang.Class 或 者 java.lang.reflect.Constructor 类 的 newInstance()实例方法。

3、 调用对象的 clone()方法。

4、运用反序列化手段,调用 java.io.ObjectInputStream 对象的 readObject()方法。 (1)和(2)都会明确的显式的调用构造函数 ;(3)是在内存上对已有对象的影印,所以不 会调用构造函数 ;(4)是从文件中还原类的对象,也不会调用构造函数

11.java注解

https://blog.csdn.net/qq1404510094/article/details/80577555

11.1 概念

注解是一系列元数据,它提供数据用来解释程序代码,但是注解并非是所解释的代码本身的一部分。注解对于代码的运行效果没有直接影响。

注解有许多用处,主要如下:

  • 提供信息给编译器: 编译器可以利用注解来探测错误和警告信息
  • 编译阶段时的处理: 软件工具可以用来利用注解信息来生成代码、Html文档或者做其它相应处理。
  • 运行时的处理: 某些注解可以在程序运行的时候接受代码的提取

11.2 4种标准元注解

元注解的作用是负责注解其他注解。 Java5.0 定义了 4 个标准的 meta-annotation 类型,它们被 用来提供对其它 annotation 类型作说明。

@Target 修饰的对象范围 @Target说明了Annotation所修饰的对象范围: Annotation可被用于 packages、types(类、 接口、枚举、Annotation 类型)、类型成员(方法、构造方法、成员变量、枚举值)、方法参数 和本地变量(如循环变量、catch 参数)。在 Annotation 类型的声明中使用了 target 可更加明晰 其修饰的目标

@Retention 定义 被保留的时间长短 Retention 定义了该 Annotation 被保留的时间长短:表示需要在什么级别保存注解信息,用于描 述注解的生命周期(即:被描述的注解在什么范围内有效),取值(RetentionPoicy)由:  SOURCE:在源文件中有效(即源文件保留)  CLASS:在 class 文件中有效(即 class 保留)  RUNTIME:在运行时有效(即运行时保留) @Documented 描述-javadoc

@ Documented 用于描述其它类型的 annotation 应该被作为被标注的程序成员的公共 API,因 此可以被例如 javadoc 此类的工具文档化。

@Inherited 阐述了某个被标注的类型是被继承的 @Inherited 元注解是一个标记注解,@Inherited 阐述了某个被标注的类型是被继承的。如果一 个使用了@Inherited 修饰的 annotation 类型被用于一个 class,则这个 annotation 将被用于该 class 的子类。

11.3 注解处理器

如果没有用来读取注解的方法和工作,那么注解也就不会比注释更有用处了。使用注解的过程中, 很重要的一部分就是创建于使用注解处理器。Java SE5 扩展了反射机制的 API,以帮助程序员快速 的构造自定义注解处理器。下面实现一个注解处理器。

 /1:*** 定义注解*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FruitProvider {
 /**供应商编号*/
public int id() default -1;
/*** 供应商名称*/
 public String name() default "";
 /** * 供应商地址*/
 public String address() default "";
}
//2:注解使用
public class Apple {
 @FruitProvider(id = 1, name = "陕西红富士集团", address = "陕西省西安市延安路")
 private String appleProvider;
 public void setAppleProvider(String appleProvider) {
 this.appleProvider = appleProvider;
 }
 public String getAppleProvider() {
 return appleProvider;
 }
}
/3:*********** 注解处理器 ***************/
public class FruitInfoUtil {
 public static void getFruitInfo(Class<?> clazz) {
 String strFruitProvicer = "供应商信息:";
 Field[] fields = clazz.getDeclaredFields();//通过反射获取处理注解
 for (Field field : fields) {
 if (field.isAnnotationPresent(FruitProvider.class)) {
 FruitProvider fruitProvider = (FruitProvider) field.getAnnotation(FruitProvider.class);
//注解信息的处理地方
strFruitProvicer = " 供应商编号:" + fruitProvider.id() + " 供应商名称:"
 + fruitProvider.name() + " 供应商地址:"+ fruitProvider.address();
 System.out.println(strFruitProvicer);
 }
 }
 }
}
public class FruitRun {
 public static void main(String[] args) {
 FruitInfoUtil.getFruitInfo(Apple.class);
/***********输出结果***************/
// 供应商编号:1 供应商名称:陕西红富士集团 供应商地址:陕西省西安市延
 }
}

11.4 注解与反射

注解通过反射获取。首先可以通过 Class 对象的 isAnnotationPresent() 方法判断它是否应用了某个注解

public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) {}

然后通过 getAnnotation() 方法来获取 Annotation 对象。

 public <A extends Annotation> A getAnnotation(Class<A> annotationClass) {}

或者是 getAnnotations() 方法。

public Annotation[] getAnnotations() {}

前一种方法返回指定类型的注解,后一种方法返回注解到这个元素上的所有注解

12. java内部类

Java 类中不仅可以定义变量和方法,还可以定义类,这样定义在类内部的类就被称为内部类。根 据定义的方式不同,内部类分为静态内部类,成员内部类,局部内部类,匿名

 public class Out {
 private static int a;
 private int b;
 public static class Inner {
     public void print() {
         System.out.println(a);
     }
 }
}

 

内部类四种。

12.1 静态内部类

定义在类内部的静态类,就是静态内部类。

1. 静态内部类可以访问外部类所有的静态变量和方法,即使是 private 的也一样。

2. 静态内部类和一般类一致,可以定义静态变量、方法,构造方法等。

3. 其它类使用静态内部类需要使用“外部类.静态内部类”方式,如下所示:Out.Inner inner = new Out.Inner();inner.print();

4. Java集合类HashMap内部就有一个静态内部类Entry。Entry是HashMap存放元素的抽象, HashMap 内部维护 Entry 数组用了存放元素,但是 Entry 对使用者是透明的。像这种和外部 类关系密切的,且不依赖外部类实例的,都可以使用静态内部类。

 静态内部类和内部类有什么区别

答: 静态内部类不需要有指向外部类的引用。但非静态内部类需要持有对外部类的引用。

静态内部类可以有静态成员(方法,属性),而非静态内部类则不能有静态成员(方法,属性)。

非静态内部类能够访问外部类的静态和非静态成员。静态内部类不能访问外部类的非静态 成员,只能访问外部类的静态成员。

实例化方式不同: 1) 静态内部类:不依赖于外部类的实例,直接实例化内部类对象 2) 非静态内部类:通过外部类的对象实例生成内部类对象

12.2 成员内部类

定义在类内部的非静态类,就是成员内部类。成员内部类不能定义静态方法和变量(final 修饰的 除外)。这是因为成员内部类是非静态的,类初始化的时候先初始化静态成员,如果允许成员内 部类定义静态变量,那么成员内部类的静态变量初始化顺序是有歧义的。

public class Out {
     private static int a;
     private int b;
     public class Inner {
         public void print() {
             System.out.println(a);
             System.out.println(b);
         }
     }
}

12.3 局部内部类

 public class Out {
     private static int a;
     private int b;
     public void test(final int c) {
         final int d = 1;
             class Inner {
                 public void print() {
                     System.out.println(c);
                 }
             }
     }
}

12.4 匿名内部类类(要继承一个父类或者实现一个接口、直接使用 new 来生成一个对象的引用)

匿名内部类我们必须要继承一个父类或者实现一个接口,当然也仅能只继承一个父类或者实现一 个接口。同时它也是没有 class 关键字,这是因为匿名内部类是直接使用 new 来生成一个对象的引用

 public abstract class Bird {
     private String name;
     public String getName() {
         return name;
     }
     public void setName(String name) {
     this.name = name;
 }
     public abstract int fly();
}
 public class Test {
     public void test(Bird bird){
         System.out.println(bird.getName() + "能够飞 " + bird.fly() + "米");
     }
      public static void main(String[] args) {
         Test test = new Test();
         test.test(new Bird() {
             public int fly() {
                 return 10000;
             }
             public String getName() {
                 return "大雁";
             }
         });
 }
}

13. java泛型

泛型,即“参数化类型”。顾名思义,就是将类型由原来的具体的类型参数化,此时类型也定义成参数形式(可以称之为类型形参),然后在使用/调用时传入具体的类型(类型实参)。泛型的本质是为了参数化类型。也就是说在泛型使用过程中,操作的数据类型被指定为一个参数,这种参数类型可以用在类、接口和方法中,分别被称为泛型类、泛型接口、泛型方法。比如我们要写一个排序方法, 能够对整型数组、字符串数组甚至其他任何类型的数组进行排序,我们就可以使用 Java 泛型。

13.1.泛型方法(<E>)

你可以写一个泛型方法,该方法在调用时可以接收不同类型的参数。根据传递给泛型方法的参数 类型,编译器适当地处理每一个方法调用。

// 泛型方法 printArray
 public static<E> void printArray(E[] inputArray )
 {
     for ( E element : inputArray ){
         System.out.printf( "%s ", element );
     }
 }

1. 表示该通配符所代表的类型是 T 类型的子类。

2. 表示该通配符所代表的类型是 T 类型的父类。

13.2 泛型类<T>

泛型类的声明和非泛型类的声明类似,除了在类名后面添加了类型参数声明部分。和泛型方法一 样,泛型类的类型参数声明部分也包含一个或多个类型参数,参数间用逗号隔开。一个泛型参数, 也被称为一个类型变量,是用于指定一个泛型类型名称的标识符。因为他们接受一个或多个参数, 这些类被称为参数化的类或参数化的类型。

public class Box<T> {
     private T t;
     public void add(T t) {
         this.t = t;
     }
     public T get() {
         return t;
     }
}

13.3 类型通配符

类型通配符? 类型通配符一般是使用 ? 代 替 具 体 的 类 型 参 数 。 例 如 List 在逻辑上是 List,List 等所有 List的父类。

13.4 类型擦除

Java 中的泛型基本上都是在编译器这个层次来实现的。在生成的 Java 字节代码中是不包含泛 型中的类型信息的。使用泛型的时候加上的类型参数,会被编译器在编译的时候去掉。这个过程就称为类型擦除。如在代码中定义的 List和 List等类型,在编译之后 都会变成 List。JVM 看到的只是 List,而由泛型附加的类型信息对 JVM 来说是不可见的。 类型擦除的基本过程也比较简单,首先是找到用来替换类型参数的具体类。这个具体类一般 是 Object。如果指定了类型参数的上界的话,则使用这个上界。把代码中的类型参数都替换 成具体的类。

其实不明白类型擦除后虚拟机如何识别某个对象的类型,但网上没搜到。

14. Java序列化(创建可复用的 Java 对象)

保存(持久化)对象及其状态到内存或者磁盘

Java 平台允许我们在内存中创建可复用的 Java 对象,但一般情况下,只有当 JVM 处于运行时, 这些对象才可能存在,即,这些对象的生命周期不会比 JVM 的生命周期更长。但在现实应用中, 就可能要求在JVM停止运行之后能够保存(持久化)指定的对象,并在将来重新读取被保存的对象。 Java 对象序列化就能够帮助我们实现该功能。

序列化对象以字节数组保持-静态成员不保存

使用 Java 对象序列化,在保存对象时,会把其状态保存为一组字节,在未来,再将这些字节组装 成对象。必须注意地是,对象序列化保存的是对象的”状态”,即它的成员变量。由此可知,对 象序列化不会关注类中的静态变量。

序列化用户远程对象传输

除了在持久化对象时会用到对象序列化之外,当使用 RMI(远程方法调用),或在网络中传递对象时, 都会用到对象序列化。Java序列化API为处理对象序列化提供了一个标准机制,该API简单易用。

Serializable 实现序列化

在 Java 中,只要一个类实现了 java.io.Serializable 接口,那么它就可以被序列化。

ObjectOutputStream 和 ObjectInputStream 对对象进行序列化及反序列化

通过 ObjectOutputStream 和 ObjectInputStream 对对象进行序列化及反序列化

writeObject 和 readObject 自定义序列化策略

在类中增加 writeObject 和 readObject 方法可以实现自定义序列化策略。

序列化 ID

虚拟机是否允许反序列化,不仅取决于类路径和功能代码是否一致,一个非常重要的一点是两个 类的序列化 ID 是否一致(就是 private static final long serialVersionUID)

序列化并不保存静态变量

序列化子父类说明

要想将父类对象也序列化,就需要让父类也实现 Serializable 接口。

Transient 关键字阻止该变量被序列化到文件中

1. 在变量声明前加上 Transient 关键字,可以阻止该变量被序列化到文件中,在被反序列 化后,transient 变量的值被设为初始值,如 int 型的是 0,对象型的是 null。

2. 服务器端给客户端发送序列化对象数据,对象中有一些数据是敏感的,比如密码字符串 等,希望对该密码字段在序列化时,进行加密,而客户端如果拥有解密的密钥,只有在 客户端进行反序列化时,才可以对密码进行读取,这样可以一定程度保证序列化对象的 数据安全。

15. java复制

将一个对象的引用复制给另外一个对象,一共有三种方式。第一种方式是直接赋值,第二种方式 是浅拷贝,第三种是深拷贝。所以大家知道了哈,这三种概念实际上都是为了拷贝对象。

15.1 直接赋值复制

直接赋值。在 Java 中,A a1 = a2,我们需要理解的是这实际上复制的是引用,也就是 说 a1 和 a2 指向的是同一个对象。因此,当 a1 变化的时候,a2 里面的成员变量也会跟着变化。

15.2 浅复制(复制引用但不复制引用的对象)

创建一个新对象,然后将当前对象的非静态字段复制到该新对象,如果字段是值类型的, 那么对该字段执行复制;如果该字段是引用类型的话,则复制引用但不复制引用的对象。 因此,原始对象及其副本引用同一个对象。

class Resume implements Cloneable{
     public Object clone() {
         try {
             return (Resume)super.clone();
         } catch (Exception e) {
             e.printStackTrace();
             return null;
         }
     }
}

15.3 深复制(复制对象和其引用对象)

class Student implements Cloneable {
     String name;
     int age;
     Professor p;
     Student(String name, int age, Professor p) {
         this.name = name;
         this.age = age;
         this.p = p;
     }
     public Object clone() {
     Student o = null;
     try {
         o = (Student) super.clone();
     } catch (CloneNotSupportedException e) {
         System.out.println(e.toString());
     }
     o.p = (Professor) p.clone();
     return o;
     }
}

深拷贝不仅复制对象本身,而且复制对象包含的引用指向的所有对象。

15.4 序列化

在 Java 语言里深复制一个对象,常常可以先使对象实现 Serializable 接口,然后把对 象(实际上只是对象的一个拷贝)写到一个流里,再从流里读出来,便可以重建对象。

16.java IO

把一个对象写入数据源或者从一个数据源读出来,用哪两个流

ObjectInputStream ObjectOutputStream

java中IO流分为几种

  • 按照流的流向分,可以分为输入流和输出流;
  • 按照操作单元划分,可以划分为字节流和字符流;
  • 按照流的角色划分为节点流和处理流。

Java Io流共涉及40多个类,这些类看上去很杂乱,但实际上很有规则,而且彼此之间存在非常紧密的联系, Java I0流的40多个类都是从如下4个抽象类基类中派生出来的。

  • InputStream/Reader: 所有的输入流的基类,前者是字节输入流,后者是字符输入流。
  • OutputStream/Writer: 所有输出流的基类,前者是字节输出流,后者是字符输出流。

按操作方式分类结构图:

按操作对象分类结构图:

BIO、NIO、AIO有什么区别?

https://blog.csdn.net/ty497122758/article/details/78979302

  • BIO (Blocking I/O): 同步阻塞I/O模式,数据的读取写入必须阻塞在一个线程内等待其完成。在活动连接数不是特别高(小于单机1000)的情况下,这种模型是比较不错的,可以让每一个连接专注于自己的 I/O 并且编程模型简单,也不用过多考虑系统的过载、限流等问题。线程池本身就是一个天然的漏斗,可以缓冲一些系统处理不了的连接或请求。但是,当面对十万甚至百万级连接的时候,传统的 BIO 模型是无能为力的。因此,我们需要一种更高效的 I/O 处理模型来应对更高的并发量。
  • NIO (New I/O): NIO是一种同步非阻塞的I/O模型,在Java 1.4 中引入了NIO框架,对应 java.nio 包,提供了 Channel , Selector,Buffer等抽象。NIO中的N可以理解为Non-blocking,不单纯是New。它支持面向缓冲的,基于通道的I/O操作方法。 NIO提供了与传统BIO模型中的 SocketServerSocket 相对应的 SocketChannelServerSocketChannel 两种不同的套接字通道实现,两种通道都支持阻塞和非阻塞两种模式。阻塞模式使用就像传统中的支持一样,比较简单,但是性能和可靠性都不好;非阻塞模式正好与之相反。对于低负载、低并发的应用程序,可以使用同步阻塞I/O来提升开发速率和更好的维护性;对于高负载、高并发的(网络)应用,应使用 NIO 的非阻塞模式来开发
  • AIO (Asynchronous I/O): AIO 也就是 NIO 2。在 Java 7 中引入了 NIO 的改进版 NIO 2,它是异步非阻塞的IO模型。异步 IO 是基于事件和回调机制实现的,也就是应用操作之后会直接返回,不会堵塞在那里,当后台处理完成,操作系统会通知相应的线程进行后续的操作。AIO 是异步IO的缩写,虽然 NIO 在网络操作中,提供了非阻塞的方法,但是 NIO 的 IO 行为还是同步的。对于 NIO 来说,我们的业务线程是在 IO 操作准备好时,得到通知,接着就由这个线程自行进行 IO 操作,IO操作本身是同步的。查阅网上相关资料,我发现就目前来说 AIO 的应用还不是很广泛,Netty 之前也尝试使用过 AIO,不过又放弃了。

17.int 与 Integer 有什么区别?

答: int 是 java 提供的 8 种原始数据类型之一。Java 为每个原始类型提供了封装类, Integer 是 java 为 int 提供的封装类。int 的默认值为 0,而 Integer 的默认值为 null,即 Integer 可以区分出未赋值和值为 0 的区别,int 则无法表达出未赋值的情况,例如,要想 表达出没有参加考试和考试成绩为 0 的区别,则只能使用 Integer。在 JSP 开发中, Integer 的默认为 null,所以用 el 表达式在文本框中显示时,值为空白字符串,而 int 默 认的默认值为 0,所以用 el 表达式在文本框中显示时,结果为 0,所以,int 不适合作为 web 层的表单数据的类型。

在 Hibernate 中,如果将 OID 定义为 Integer 类型,那么 Hibernate 就可以根据其 值是否为 null 而判断一个对象是否是临时的,如果将 OID 定义为了 int 类型,还需要在 hbm 映射文件中设置其 unsaved-value 属性为 0。 另外,Integer 提供了多个与整数相关的操作方法,例如,将一个字符串转换成整数, Integer 中还定义了表示整数的最大值和最小值的常量。

18.Comparable和Comparatot的区别

Comparable是内部比较器,需要复写conpareTo方法,若一个类实现了Comparable接口,意味着该类支持排序,而Comparator是外部比较器,需要复习compare方法,我们需要控制某个类的次序,可以建立一个该类的比较器。

19.java中的参数传递机制

java中没有值传递,只传递副本。

20.static关键字的作用是什么

修饰变量:因为类加载进方法区,所以多个对象是共享的。

修饰方法:工具类的方法,不需要建立对象

修饰静态代码块:用于初始化等操作。

静态内部类

注意:一般方法可以静态方法,但静态方法只能访问静态方法。

21. hashCode

复写equals方法必须复写hashcode方法,不然的话有时候会先比较两个对象hashcode值是否相等,如果hashcode值不等,那么可能queals函数还没调用,就返回false了,复写hashcode方法,就使得hashcode值与对象属性值相关联,最好不要用易变的属性。https://www.cnblogs.com/dolphin0520/p/3681042.html讲得不错。

22.HashMap底层实现(picture10)

https://www.cnblogs.com/xiaoxi/p/7233201.html(根据k计算hash值和put操作讲得好。get和resize只写了代码和注释)

在JDK1.8之前,HashMap采用数组+链表实现s,数组是Node类型的数组,Node是HashMap的一个内部类,实现了Map.Entry接口,本质是就是一个映射(或者说键值对),,node中包含一个next变量,这个就是链表的关键点,hash结果相同的元素就是通过这个next进行关联的,同一hash值的节点都存储在一个链表里。当hash值相等的元素较多时,通过key值依次查找的效率较低。而JDK1.8中,HashMap采用数组+链表+红黑树实现,当链表长度超过阈值(8)时,将链表转换为红黑树,这样大大减少了查找时间。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值