Java基础面试总结2

jdk、jre、jvm的区别?

JDK : (Java开发工具包)。JDK是整个JAVA的核心,包括了Java运行环境(JRE),一堆Java工具(javac/java/jdb等)和Java基础的类库(即Java API 包括rt.jar)。
JRE:(java运行时环境)。也就是我们说的JAVA平台,所有的Java程序都要在JRE下才能运行。包括JVM和JAVA核心类库和支持文件。与JDK相比,它不包含开发工具——编译器、调试器和其它工具。
JVM:Java Virtual Mechinal(JAVA虚拟机)。JVM是JRE的一部分,它是一个虚构出来的计算机,是通过仿真模拟各种计算机功能来实现的。JVM有自己完善的硬件架构,如处理器、堆栈、寄存器等,还具有相应的指令系统。JVM 的主要工作是解释自己的指令集(即字节码)并映射到本地的 CPU 的指令集或 OS 的系统调用。

== 和 equals 有什么区别?

 == 比较的是左右两边的值,如果是基本类型,比较的就是字面值,如果是引用类型,比较的是引用类型变量保存的地址值
 equals()比较两个对象的类型+所有属性与属性值

如果是基本类型,直接比较值,所以用==比较
如果是引用类型,比如String,还需要使用bjects.equals()做比较

构造代码块:

位置:类里方法外
执行时机:每次创建对象时执行,并且优先于构造方法执行
作用:用于提取所有构造方法的共性功能

4.局部代码块:

位置:方法里
执行时机:调用本局部代码块所处的方法时执行
作用:用于控制变量的作用范围
执行顺序:

构造代码块->构造方法->普通方法->局部代码块

this的用法:

1.当成员变量与局部变量同名时,使用this指定成员变量
2. This表示当前类的对象
this();–-调用的是本类的无参构造
this(参数);–调用的是本类对应参数的构造方法

5.2 super的用法:

1.当父类的成员变量与子类的变量同名时,使用super指定父类的成员变量
2.Super表示当前类的父类对象
super();–调用的是父类的无参构造
super(参数);–调用的是父类对应参数的构造方法

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

不对,两个对象的 hashCode() 相同,equals() 不一定 true。
代码示例:

String str1 = "通话";
String str2 = "重地";
System. out. println(String. format("str1:%d | str2:%d", 
str1. hashCode(),str2. hashCode()));
System. out. println(str1. equals(str2));
执行的结果:
str1:1179395 | str2:1179395
false

很显然“通话”和“重地”的 hashCode() 相同,然而 equals() 则
为 false,因为在散列表中,hashCode() 相等即两个键值对的哈希值相等,然
而哈希值相等,并不一定能得出键值对相等

final, finally, finalize的区别。

答案:
1、性质不同
(1)final为关键字;
(2)finalize()为方法;
(3)finally为为区块标志,用于try语句中;
2、作用
(1)final为用于标识常量的关键字,final标识的关键字存储在常量池中
(2)finalize()方法在Object中进行了定义,用于在对象“消失”时,由JVM进行调用用于对对象进行垃圾回收,类似于C++中的析构函数;用户自定义时,用于释放对象占用的资源(比如进行I/0操作);
(3)finally{}用于标识代码块,与try{}进行配合,不论try中的代码执行完或没有执行完(这里指有异常),该代码块之中的程序必定会进行;
3、final详解

在使用final修饰的变量,是常量不能被修改,
使用final修饰的方法,不让任何继承类对其进行修改
使用final修饰的累不能被继承;

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

注意:Math的round方法是四舍五入,如果参数是负数,则往大的数如,Math.round(-1.5)=-1,如果是Math.round(1.5)则结果为2

java 中操作字符串都有哪些类?它们之间有什么区别?

三者在执行速度方面的比较:StringBuilder > StringBuffer > String
String类是不可变类,即字符串值一旦初始化后就不可能改变。StringBuffer是可变字符串类,类似String的缓冲区,可以修改字符串的值。
StringBuilder中的方法和StringBuffer中的方法基本相同,但是StringBuffer是线程安全的,而StringBuilder不是线程安全的,因此在不考虑同步的情况下,StringBuilder有更好的性能

String str=“i“与 String str=new String(“i“)一样吗?

不一样,因为内存的分配方式不一样。 String str=“i” 的方式, Java 虚拟机会将其分配到常量池中;而
String str=new String(“i”) 则会被分到堆内存中。
代码示例:

String x = " 叶痕秋 " ;
String y = " 叶痕秋 " ;
String z = new String ( " 叶痕秋 " );
System . out . println ( x == y ); // true
System . out . println ( x == z ); // false

String x = " 叶痕秋 " 的方式, Java 虚拟机会将其分配到常量池中,而常量池中没有重复的元素,比如当
执行 “ 叶痕秋 ” 时, java 虚拟机会先在常量池中检索是否已经有 “ 叶痕秋 ” ,如果有那么就将 “ 叶痕秋 ” 的地址
赋给变量,如果没有就创建一个,然后在赋给变量;
而 String z = new String(“ 叶痕秋 ”) 则会被分到堆内存中,即使内容一样还是会创建新的对象

反转的方法

StringBuilder的reverse()方法,最简单

public static String reverse4(String s) {

  return new StringBuffer(s).reverse().toString();
}

3.使用String的CharAt方法,使用String的CharAt方法取出字符串中的各个字符,然后插入到字符串中,调用StringBuilder的insert()方法进行操作。

4.使用递归的方法,实现字符串反转。

public static String reverse1(String s) {

  int length = s.length();

  if(length <= 1){

    return s;

   }

  String left = s.substring(0, length / 2);

  String right = s.substring(length / 2, length);

  return reverse1(right) + reverse1(left);

}

chartAt:返回字符串中指定位置的字符;注意字符串中第⼀个字符索引是0,最后⼀个是length()-1。

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

抽象类不一定有抽象方法;但是包含一个抽象方法的类一定是抽象类。(有抽象方法就是抽象类,是抽象类可以没有抽象方法)

抽象类和普通类区别:

1、都有构造函数,但是抽象类不可实例化,必须通过实例化子类时才会调用父类构造器。
2、抽象类可以没有抽象方法,也可以有普通方法;但是普通类一定没有抽象方法 。
3、抽象方法不可以是static的,但普通方法可以。
4、如果普通类继承了抽象类,那么必须要重写所有父类的抽象方法

抽象类能使用 final 修饰吗?

不能,定义抽象类就是让其他类继承的,如果定义为 final 该类就不能被继承,这样彼此就会产生矛盾,所以 final 不能修饰抽象类,如下图所示,编辑器也会提示错误信息:

解释:
final修饰的类,不能被继承,没有子类,如果类中有抽象方法也是没有意义的,abstrsct类是抽象类,即该类只关心子类具有的功能,而不是功能的具体实现,如果用fial修饰的方法,则该方法不能被重写,是不能用abstract所修饰的方法的;

抽象类和接口的区别有哪些

抽象类要被子类继承,接口要被类实现
接口只能做方法声明,抽象类中可以作方法声明,也可以做方法实现。
接口里定义的变量只能是公共的静态的常量,抽象类中的变量是普通变量。
接口是设计的结果,抽象类是重构的结果。
抽象类和接口都是用来抽象具体对象的,但是接口的抽象级别最高。
抽象类可以有具体的方法和属性,接口只能有抽象方法和不可变常量。
抽象类主要用来抽象类别,接口主要用来抽象功能。

接口与接口的关系
继承关系,可以单继承,也可以多继承

6. 继承

继承的关键字extends 格式: 子类 extends 父类
继承相当于子类把父类的功能复制了一份
Java只支持单继承:一个子类只能有一个父类,一个父类可以有多个子类
继承具有传递性:爷爷的功能会传给爸爸,爸爸的功能会传给孙子
子类只可以使用父类的非私有资源,私有资源由于private限制,不可用
子类可以拥有自己的特有功能
继承是is a 强耦合的关系,依赖性非常强,

父类成员变量与子类成员变量同名时,使用super.变量名指定父类的成员变量

继承的好处

1、继承的出现提高了代码的复用性,提高软件开发效率。
2、继承的出现让类与类之间产生了关系,提供了多态的前提。

7 继承中的构造方法

子类在创建对象时,默认会先调用父类的构造方法
原因是子类构造函数中的第1行默认存在super();–表示调用父类的无参构造
当父类没有无参构造时,可以通过super(参数);调用父类的其他含参构造
注意:子类必须调用父类的一个构造函数,不论是无参还是含参,选一个即可
构造方法不可以被继承!因为语法的原因,要求构造方法的名字必须是本类类名
不能在子类中出现一个父类名字的构造方法

2内部类特点

  1. 内部类可以直接访问外部类中的成员,包括私有成员
  2. 外部类要访问内部类的成员,必须要建立内部类的对象
  3. 在成员位置的内部类是成员内部类
  4. 在局部位置的内部类是局部内部类

java 中 IO 流分为几种?

Java中的流分为两种,一种是字节流,另一种是字符流,分别由四个抽象类来表示(每种流包括输入和输出两种所以一共四个),Java中其他多种多样变化的流均是由它们派生出来的.

字节流:InputStream,OutputStream。

字符流: Reader,Writer。

二、字符流和字节流的区别

字符流和字节流是根据处理数据的类型的不同来区分的。

字节流按照8位传输,字节流是最基本的,所有文件的储存是都是字节(byte)的储存,在磁盘上保留的并不是文件的字符而是先把字符编码成字节,再储存这些字节到磁盘。

字节流可用于任何类型的对象,包括二进制对象,而字符流只能处理字符或者字符串;

字节流提供了处理任何类型的IO操作的功能,但它不能直接处理Unicode字符,而字符流就可以。

理论上任何文件都能够用字节流读取,但当读取的是文本数据时,为了能还原成文本你必须再经过一个转换的工序,相对来说字符流就省了这个麻烦,可以有方法直接读取。所以,如果是处理纯文本数据,就要优先考虑字符流,除此之外都是用字节流。

BIO、NIO、AIO 有什么区别?

简单介绍
BIO 就是传统的 java.io包,它是基于流模型实现的,交互的方式是同步、阻塞方式,也就是说在读入输入流或者输出流时,在读写动作完成之前,线程会一直阻塞在那里,它们之间的调用时可靠的线性顺序。它的优点就是代码比较简单、直观;缺点就是 IO 的效率和扩展性很低,容易成为应用性能瓶颈。
NIO 是 Java 1.4 引入的 java.nio 包,提供了 Channel、Selector、Buffer等新的抽象,可以构建多路复用的、同步非阻塞 IO 程序,同时提供了更接近操作系统底层高性能的数据操作方式。
AIO 是 Java 1.7 之后引入的包,是 NIO 的升级版本,提供了异步非堵塞的 IO 操作方式,所以人们叫它AIO(Asynchronous IO),异步 IO是基于事件和回调机制实现的,也就是应用操作之后会直接返回,不会堵塞在那里,当后台处理完成,操作系统会通知相应的线程进行后续的操作。
详细介绍
本文内容涉及同步与异步,阻塞与非阻塞,BIO、NIO、AIO等概念,这块内容本身比较复杂,很难用三言两语说明白,而书上的定义更不容易理解是什么意思。下面这些内容是按照我的理解,以我认为尽可能简单易懂的语言组织出来,希望能够帮助理解这些概念。

  1. 同步与异步

同步与异步的概念,关注的是 消息通信机制

同步是指发出一个请求,在没有得到结果之前该请求就不返回结果,请求返回时,也就得到结果了。
比如洗衣服,把衣服放在洗衣机里,没有洗好之前我们一直看着, 直到洗好了才拿出来晾晒。

异步是指发出一个请求后,立刻得到了回应,但没有返回结果。这时我们可以再处理别的事情(发送其他请求),所以这种方式需要我们通过状态主动查看是否有了结果, 或者可以设置一个回调来通知调用者。

比如洗衣服时,把衣服放到洗衣机里,我们就可以去做别的事情,过会儿来看看有没有洗好(通过状态查询);或者我们设置洗衣机洗完后响铃来通知我们洗好了(回调通知)

  1. 阻塞与非阻塞

阻塞与非阻塞很容易和同步与异步混淆,但两者关注点是不一样的。 阻塞与非阻塞关注的是 程序在等待调用结果时的状态

阻塞是指请求结果返回之前,当前线程会被挂起(被阻塞),这时线程什么也做不了
非阻塞是指请求结果返回之前,当前线程没有被阻塞,仍然可以做其他事情。
阻塞有个明显的特征就是线程通常是处于BLOCKED状态(BIO中的read()操作时,线程阻塞是JVM配合OS完成的,此时Java获取到线程的状态仍是RUNNABLE但它确实已经被阻塞了)

如果要拿同步来做比较的话,同步通信方式中的线程在发送请求之后等待结果这个过程中应该处于RUNNABLE状态,同步必须一步一步来完成,就像是代码必须执行完一行才能执行下一行, 所以必须等待这个请求返回之后才可进行下一个请求, 即使等待结果的时间长,也是在执行这个请求的过程中。而异步则不用等上一条执行完, 可以先执行别的代码,等请求有了结果再来获取结果。

  1. IO模型

Java中的IO操作是JVM配合操作系统来完成的。对于一个IO的读操作,数据会先被拷贝到操作系统内核的缓冲区中,然后从操作系统内核的缓冲区拷贝到应用程序的地址空间。所以整个过程可分为两个阶段:

等待I/O数据准备好,这取决于IO目标返回数据的速度, 如网络IO时看网速和数据本身的大小。
数据从内核缓冲区拷贝到进程内。
根据这两个阶段,产生了常见的几种不同的IO模型:BIO, NIO, IO多路复用和AIO。

3.1 BIO
BIO即Blocking I/O(阻塞 I/O),BIO整个过程如下图:
在这里插入图片描述

程序发送请求给内核,然后由内核去进行通信,在内核准备好数据之前这个线程是被挂起的,所以在两个阶段程序都处于挂起状态。

BIO的特点就是在IO执行的两个阶段都被block了

3.2 NIO

NIO即Non-Blocking I/O(非阻塞 I/O), NIO整个过程如下图:

在这里插入图片描述

与BIO的明显区别是,发起第一次请求后,线程并没有被阻塞,它反复检查数据是否准备好,把原来大块不能用的阻塞时间分成了许多“小阻塞”(检查),所以进程不断有机会被执行。这个检查有没有准备好数据的过程有点类似于“轮询”。

NIO的特点就是程序需要不断的主动询问内核数据是否准备好。第一个阶段非阻塞,第二个阶段阻塞
3.3 IO多路复用

IO多路复用(I/O Multiplexing)有select,poll,epoll等不同方式,它的优点在于单个线程可以同时处理多个网络IO。

NIO中轮询操作是用户线程进行的,如果把这个任务交给其他线程,则用户线程就不用这么费劲的查询状态了。IO多路复用调用系统级别的select或poll模型,由系统进行监控IO状态。select轮询可以监控许多socket的IO请求,当有一个socket的数据准备好时就可以返回。

select: 注册事件由数组管理, 数组是有长度的, 32位机上限1024, 64位机上限2048。轮询查找时需要遍历数组。

poll:把select的数组采用链表实现,因此没了最大数量的限制

epoll方式:基于事件回调机制,回调时直接通知进程,无须使用某种方式来查看状态。

多路复用IO过程图:

在这里插入图片描述

用户线程有一段时间是阻塞的,从上图来看,与NIO很像,但与NIO不一样的是,select不是等到所有数据准备好才返回,而是只要有一个准备好就返回,它的优势在于可以同时处理多个连接。若连接不是很多的话,它的效率不一定高,可能还会更差。

Java 1.4开始支持NIO(New IO),就是采用了这种方式,在套接字上提供selector选择机制,当发起select() 时会阻塞等待至少一个事件返回。

多路复用IO的特点是用户进程能同时等待多个IO请求,系统来监控IO状态,其中的任意一个进入读就绪状态,select函数就可以返回。
3.4 AIO

AIO即Asynchronous I/O(异步 I/O),这是Java 1.7引入的NIO 2.0中用到的。整个过程中,用户线程发起一个系统调用之后无须等待,可以处理别的事情。由操作系统等待接收内容,接收后把数据拷贝到用户进程中,最后通知用户程序已经可以使用数据了,两个阶段都是非阻塞的。AIO整个过程如下图:

在这里插入图片描述

AIO属于异步模型, 用户线程可以同时处理别的事情,我们怎么进一步加工处理结果呢? Java在这个模型中提供了两种方法:

一种是基于”回调”,我们可以实现CompletionHandler接口,在调用时把回调函数传递给对应的API即可

另一种是返回一个Future。处理完别的事情,可以通过isDone() 可查看是否已经准备好数据,通过get()方法等待返回数据。

Files的常用方法都有哪些?

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

什么是反射

Reflection(反射) 是 反射是指在运行时能查看一个类的状态及特征,并能进行动态管理的功能。它甚至能直接操作程序的私有属性(比如被private修饰的属性),还可以获取到任何定义的信息(包括成员变量,成员方法,构造器等),并且可以操纵类的字段、方法、构造器等部分。

Class.forName(“类的全路径”);
类名.class
对象.getClass();

序列化和反序列化

序列化:将 Java 对象转换成字节流的过程。
反序列化:将字节流转换成 Java 对象的过程。

什么时候需要序列化

当 Java 对象需要在网络上传输 或者 持久化存储到文件中时,就需要对 Java 对象进行序列化处理。
序列化的实现:类实现 Serializable 接口,这个接口没有需要实现的方法。实现 Serializable 接口是为了告诉 jvm 这个类的对象可以被序列化。

动态代理是什么?有哪些应用?

动态代理:当想要给实现了某个接口的类中的方法,加一些额外的处理。比如说加日志,加事务等。可以给这个类创建一个代理,故名思议就是创建一个新的类,这个类不仅包含原来类方法的功能,而且还在原来的基础上添加了额外处理的新类。这个代理类并不是定义好的,是动态生成的。具有解耦意义,灵活,扩展性强。

动态代理实现:首先必须定义一个接口,还要有一个InvocationHandler(将实现接口的类的对象传递给它)处理类。再有一个工具类Proxy(习惯性将其称为代理类,因为调用他的newInstance()可以产生代理对象,其实他只是一个产生代理对象的工具类)。利用到InvocationHandler,拼接代理类源码,将其编译生成代理类的二进制码,利用加载器加载,并将其实例化产生代理对象,最后返回。

写自己的MyInvocationHandler实现InvocationHandler接口,

重写invoke方法来添加我们需要添加的额外功能

动态代理的应用:Spring的AOP,加事务,加权限,加日志。

1.动态代理和静态代理的区别
静态代理:在编译之前就已经确定好代理对象,代理方法等等

动态代理:在编译后才明确代理对象以及代理方法等等

2.JDK代理原理

在这里插入图片描述

使用JDK动态代理方法,我们需要代理类和被代理类同时继承同一个接口才能进行增强。

3.CGlib代理原理

在这里插入图片描述

JDK动态代理时,必须提供一个接口,但是在有些环境中,我们并不能提供一个接口,只能采用第三方的代理方法CGlib。它的优势是只要提供一个非抽象类,即可进行增强处理。

JDK代理类

继承实现InvocationHandler接口

Proxy.newProxyInstance参数说明

第一个参数 本类的类加载器

第二个参数 被代理类实现的所有接口

第三个参数 本类

invoke方法中的参数说明

proxy 被代理的对象

method 将要被执行的方法信息(反射)

args 执行方法需要的参数
CGlib代理类

注意引入的jar:import org.springframework.cglib.proxy.MethodInterceptor;

继承实现MethodInterceptor

inertcept参数说明

o 根据指定父类形成代理对象

method 拦截的方法

objects 方法的参数

methodProxy 方法的代理对象,用于执行父类方法

throw 和 throws 的区别?

throw:

表示方法内抛出某种异常对象
如果异常对象是非 RuntimeException 则需要在方法申明时加上该异常的抛出 即需要加上 throws 语句 或者 在方法体内 try catch 处理该异常,否则编译报错
执行到 throw 语句则后面的语句块不再执行

throws:
方法的定义上使用 throws 表示这个方法可能抛出某种异常
需要由方法的调用者进行异常处理

26、简述static,final,finally,finalize的作用和区别

final关键字
1、final修饰类,表示该类不能被继承,因此,一个类不能同事被声明为abstract抽象类和final的类;
2、final修饰变量,则该变量必须赋初值,而且它的取值在整个过程都不会改变;
3、final修饰方法,称为最终方法。它不可被子类重写,也不能被重载;
static关键字
(1)static定义的数据或方法,可以不用new出类的实例而让类直接调用;
(2)static代码块: 用来形成静态代码块以优化程序性能。
finally:java的一种异常处理机制。
(1) finally是对Java 异常处理模型的最佳补充。finally 结构使代码总会执行,而不管有无异常发生。
(2)使用 finally 可以维护对象的内部状态,并可以清理非内存资源。
(3)特别是在关闭数据库连接这方面,如果程序员把数据库连接的close()方法放到finally中,就会大大降低程序出错的几率。
finalize:Java中Object类中的一个方法名。
Java技术使用finalize()方法在垃圾收集器将对象从内存中清除出去,做必要的清理工作。
这个方法是由垃圾收集器在确定这个对象没有被引用时对这个对象调用的。
它是在Object类中定义的,因此所有的类都继承了它。子类覆盖finalize()方法以整理系统资源或者执行其他清理工作。
finalize()方法是在垃圾收集器删除对象之前对这个对象调用的。

try-catch-finally 中,如果 catch 中 return 了,finally 还会执行吗?

finally 一定会执行
try-catch-finally中return的执行情况:

在try中没有异常的情况下try、catch、finally的执行顺序 try — finally

如果try中有异常,执行顺序是try — catch — finally

如果try中没有异常并且try中有return这时候正常执行顺序是try ---- finally — return

如果try中有异常并且try中有return这时候正常执行顺序是try----catch—finally— return

总之 finally 永远执行!

不管有没有异常,finally中的代码都会执行
当try、catch中有return时,finally中的代码依然会继续执行
finally是在return后面的表达式运算之后执行的,此时并没有返回运算之后的值,而是把值保存起来,不管finally对该值做任何的改变,返回的值都不会改变,依然返回保存起来的值。也就是说方法的返回值是在finally运算之前就确定了的。
如果return的数据是引用数据类型,而在finally中对该引用数据类型的属性值的改变起作用,try中的return语句返回的就是在finally中改变后的该属性的值。
finally代码中最好不要包含return,程序会提前退出,也就是说返回的值不是try或catch中的值
return的返回值:

new String(“a”) + new String(“b”) 会创建几个对象?

public class StringNewTest {
    public static void main(String[] args) {
        String str = new String("a") + new String("b");
    }
}

对象1: new StringBuilder()
对象2: new String(“a”)
对象3: 常量池中的"a"
对象4: new String(“b”)
对象5: 常量池中的"b"
对象6 :new String(“ab”)
结论:是6个对象

cookie和session的区别

(1)Cookie是将会话中产生的数据保存在客户端,是客户端的技术
(2)Session是将会话中产生的数据保存在服务器端,是服务器端的技术
(3)Cookie保存的信息的时间比较长,但是安全性不高,可能随着用户的操作,Cookie会被清空,所以Cookie存储数据的稳定性比较差。因此Cookie适合存放要保存时间较长,但安全性要求不高的信息
(4)Session通常保存信息的时间比较有限,但安全性比较高,因为是保存在服务器端,不会随着用户的操作而导致Session意外丢失,因此session适合存放安全性要求比较高,但是不需要长时间保存的数据。
(5)在性能上,Cookie性能更高一些。Cookie是将数据保存在用户各自的客户端浏览器中,而Session是服务器端的对象,是将数据(在一定时间内)保存在服务器端,因此当访问量增多时,会降低服务器的性能。
(6)在数据量上,单个Cookie保存的数据量不能超过4KB,而很多浏览器都限制一个站点给浏览器最多发送20个Cookie。而Session不存在此问题。

介绍Collection框架的结构

Collection 是单列集合,包含List列表,Set集
Map:Hashtable,HashMap,TreeMap
List 元素是有序的、可重复. Set(集) 元素无序的、不可重复。
List接口中常用类
Vector: 线程安全,但速度慢,已被ArrayList替代。
底层数据结构是数组结构
ArrayList:线程不安全,查询速度快。访问任意位置,效率高
增删数据,效率可能降低
底层数据结构是数组结构,内部数组默认的初始容量 10,放满后,1.5倍增长
LinkedList:线程不安全。增删速度快。两端效率高
底层数据结构是列表结构

Set接口中常用的类
HashSet:线程不安全,存取速度快。
它是如何保证元素唯一性的呢?依赖的是元素的hashCode方法和euqals方法。

TreeSet:线程不安全,可以对Set集合中的元素进行排序。
它的排序是如何进行的呢?通过compareTo或者compare方法中的来保证元素的唯一性。元素是以二叉树的形式存放的。

Map 是一个双列集合
–Hashtable:线程安全,速度快。底层是哈希表数据结构。是同步的。
不允许null作为键,null作为值。
–HashMap:线程不安全,速度慢。底层也是哈希表数据结构。是不同步的。不重复 无序
允许null作为键,null作为值。替代了Hashtable.
–LinkedHashMap: 可以保证HashMap集合有序。存入的顺序和取出的顺序一致。
–TreeMap:可以用来对Map集合中的键进行排序.
哈希运算过程
HashMap内部,使用 Entry[] 数组存放数据,数组默认初始容量 16,放满后容量翻倍+2

key.hashCode() 获得键的哈希值
用哈希值和数组长度,运算产生一个下标值 i
新建Entry 对象来封装键值对数据
Entry对象,放在 i 位置
如果是空位置,直接放入,有数据,依次equals()比较是否相等
找到相等的,覆盖旧值,没有相等的,链表连接在一起
负载率、加载因子 0.75
新建翻倍容量的新数组
所有数据,重新执行哈希运算,放入新数组
jdk1.8
链表长度到8,转成红黑树
树上的数据减少到6,转回成链表

介绍Collection框架的结构
Collection 是单列集合,包含List列表,Set集
Map:Hashtable,HashMap,TreeMap
List 元素是有序的、可重复. Set(集) 元素无序的、不可重复。
List接口中常用类
Vector: 线程安全,但速度慢,已被ArrayList替代。
底层数据结构是数组结构
ArrayList:线程不安全,查询速度快。访问任意位置,效率高
增删数据,效率可能降低
底层数据结构是数组结构,内部数组默认的初始容量 10,放满后,1.5倍增长
LinkedList:线程不安全。增删速度快。两端效率高
底层数据结构是列表结构

Set接口中常用的类
HashSet:线程不安全,存取速度快。
它是如何保证元素唯一性的呢?依赖的是元素的hashCode方法和euqals方法。

TreeSet:线程不安全,可以对Set集合中的元素进行排序。
它的排序是如何进行的呢?通过compareTo或者compare方法中的来保证元素的唯一性。元素是以二叉树的形式存放的。

Map 是一个双列集合
–Hashtable:线程安全,速度快。底层是哈希表数据结构。是同步的。
不允许null作为键,null作为值。
–HashMap:线程不安全,速度慢。底层也是哈希表数据结构。是不同步的。不重复 无序
允许null作为键,null作为值。替代了Hashtable.
–LinkedHashMap: 可以保证HashMap集合有序。存入的顺序和取出的顺序一致。
–TreeMap:可以用来对Map集合中的键进行排序.
哈希运算过程
HashMap内部,使用 Entry[] 数组存放数据,数组默认初始容量 16,放满后容量翻倍+2

key.hashCode() 获得键的哈希值
用哈希值和数组长度,运算产生一个下标值 i
新建Entry 对象来封装键值对数据
Entry对象,放在 i 位置
如果是空位置,直接放入,有数据,依次equals()比较是否相等
找到相等的,覆盖旧值,没有相等的,链表连接在一起
负载率、加载因子 0.75
新建翻倍容量的新数组
所有数据,重新执行哈希运算,放入新数组
jdk1.8
链表长度到8,转成红黑树
树上的数据减少到6,转回成链表
请添加图片描述

说一下HashMap的实现原理

HashMap 基于 Hash 算法实现,通过 put(key,value) 存储,get(key) 来获取 value
当传入 key 时,HashMap 会根据 key,调用 hash(Object key) 方法,计算出 hash 值,根据 hash 值将 value 保存在 Node 对象里,Node 对象保存在数组里
当计算出的 hash 值相同时,称之为 hash 冲突,HashMap 的做法是用链表和红黑树存储相同 hash 值的 value
当 hash 冲突的个数:小于等于 8 使用链表;大于 8 时,使用红黑树解决链表查询慢的问题

说一下 HashSet 的实现原理?

1、是基于HashMap实现的,默认构造函数是构建一个初始容量为16,负载因子为0.75 的HashMap。封装了一个 HashMap 对象来存储所有的集合元素,所有放入 HashSet 中的集合元素实际上由 HashMap 的 key 来保存,而 HashMap 的 value 则存储了一个 PRESENT,它是一个静态的 Object 对象。

2、当我们试图把某个类的对象当成 HashMap的 key,或试图将这个类的对象放入 HashSet 中保存时,重写该类的equals(Object obj)方法和 hashCode() 方法很重要,而且这两个方法的返回值必须保持一致:当该类的两个的 hashCode() 返回值相同时,它们通过 equals() 方法比较也应该返回 true。

通常来说,所有参与计算 hashCode() 返回值的关键属性,都应该用于作为 equals() 比较的标准。

3、HashSet的其他操作都是基于HashMap的

如何实现数组和 List 之间的转换?

package listtoArray;
 
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
 
public class ArrayToList {
	
	public static void main(String[] args) {
		
		//数组转list
		String[] str=new String[] {"hello","world"};
		//方式一:使用for循环把数组元素加进list
		List<String> list=new ArrayList<String>();
		for (String string : str) {
			list.add(string);
		}
		System.out.println(list);
		
		//方式二:
		List<String> list2=new ArrayList<String>(Arrays.asList(str));
		System.out.println(list2);
		
		//方式三:
		//同方法二一样使用了asList()方法。这不是最好的,
		//因为asList()返回的列表的大小是固定的。
		//事实上,返回的列表不是java.util.ArrayList类,而是定义在java.util.Arrays中一个私有静态类java.util.Arrays.ArrayList
		//我们知道ArrayList的实现本质上是一个数组,而asList()返回的列表是由原始数组支持的固定大小的列表。
		//这种情况下,如果添加或删除列表中的元素,程序会抛出异常UnsupportedOperationException。
		//java.util.Arrays.ArrayList类具有 set(),get(),contains()等方法,但是不具有添加add()或删除remove()方法,所以调用add()方法会报错。
		List<String> list3 = Arrays.asList(str);
		//list3.remove(1);
		//boolean contains = list3.contains("s");
		//System.out.println(contains);
		System.out.println(list3);
		
		//方式四:使用Collections.addAll()
		List<String> list4=new ArrayList<String>(str.length);
		Collections.addAll(list4, str);
		System.out.println(list4);
		
		//方式五:使用Stream中的Collector收集器
		//转换后的List 属于 java.util.ArrayList 能进行正常的增删查操作
		List<String> list5=Stream.of(str).collect(Collectors.toList());
		System.out.println(list5);
	}
}

List转数组


package listtoArray;
 
import java.util.ArrayList;
import java.util.List;
 
public class ListToArray {
 
	public static void main(String[] args) {
		//list转数组
		List<String> list=new ArrayList<String>();
		list.add("hello");
		list.add("world");
		
		//方式一:使用for循环
		String[] str1=new String[list.size()];
		for(int i=0;i<list.size();i++) {
			str1[i]=list.get(i);
		}
		for (String string : str1) {
			System.out.println(string);
		}
		
		//方式二:使用toArray()方法
		//list.toArray(T[]  a); 将list转化为你所需要类型的数组
		String[] str2=list.toArray(new String[list.size()]);
		for (String string : str2) {
			System.out.println(string);
		}
		
		//错误方式:易错   list.toArray()返回的是Object[]数组,怎么可以转型为String
		//ArrayList<String> list3=new ArrayList<String>();
		//String strings[]=(String [])list.toArray();
	}
 
}

迭代器 Iterator 是什么?

它可以遍历并选择序列中的对象,而开发人员不需要了解该序列的底层结构。迭代器通常被称为“轻量级”对象,因为创建它的代价小。

Java中的Iterator功能比较简单,并且只能单向移动:

(1) 使用方法iterator()要求容器返回一个Iterator。第一次调用Iterator的next()方法时,它返回序列的第一个元素。注意:iterator()方法是java.lang.Iterable接口,被Collection继承。

(2) 使用next()获得序列中的下一个元素。

(3) 使用hasNext()检查序列中是否还有元素。

(4) 使用remove()将迭代器新返回的元素删除。

Iterator是Java迭代器最简单的实现,为List设计的ListIterator具有更多的功能,它可以从两个方向遍历List,也可以从List中插入和删除元素。

mybatis 中 #{}和 ${}的区别是什么?

1、#{}是预编译处理,${}是字符串替换

2.MyBatis在处理#{}时,会将sql中的#{}替换为?号,调用PreparedStatement的set方法来赋值

而在处理 时,是把 {}时,是把 时,是把{}替换为变量的值。

3.使用#{}可以有效地防止SQL注入,提高系统安全性;因为一个#{}被解析为一个参数占位符,而${}仅仅为一个纯粹的String替换。

使用注意:

但是在mybatis排序使用order by 动态传参时,使用${}而不用#{}.

Mybatis四种分页方式

第一种分页,全部查出来,然后在进行分页,这种分页不好
2、第二种分页,使用limit,前端传进来页码和每页的条数,在sql使用limit进行查询
3、第三种分页方式,用RowBounds分页,创建RowBounds对象,使用有参传开始的条数((page -1)*pagesize)和每页几条数据(pagesize),调用mapper方法讲RowBounds对象传递进去
4、使用pageHelper插件分页、主要是通过mybatis拦截器实现的
导入pageHelper包
使用springboot进行脚手架搭建,导入对应依赖,写对应的yaml文件

Mybatis是如何进⾏分页的?分页插件的原理是什么?

Mybatis使⽤RowBounds对象进⾏分页,
它是针对ResultSet结果集执⾏的内存分页,⽽⾮物理分页。
可以在sql内直接书写带有物理分页的参数来完成物理分页功能,
也可以使⽤分页插件来完成物理分页。
分页插件的基本原理是使⽤Mybatis提供的插件接⼝,
实现⾃定义插件,在插件的拦截⽅法内拦截待执⾏的sql,
然后重写sql,根据dialect,添加对应的物理分页语句和物理分页参数。

mybatis 逻辑分页和物理分页的区别是什么?

逻辑分页
是一次性从数据库查询很多数据,然后再在结果中检索分页的数据。

弊端:需要消耗大量的内存、有内存溢出的风险、对数据库压力较大。

2、物理分页
是从数据库查询指定条数的数据。

好处:弥补了一次性全部查出的所有数据的缺点,比如需占用大量内存,数据库查询压力较大等问题。

说一下 mybatis 的一级缓存和二级缓存?

MyBatis 的一级缓存是基于数据库会话(SqlSession 对象)的,默认开启。二级缓存是基于mapper级的,开启需要配置。

mybatis 和 hibernate 的区别有哪些?

Hibernate的优点:

1、hibernate是全自动,hibernate完全可以通过对象关系模型实现对数据库的操作,拥有完整的JavaBean对象与数据库的映射结构来自动生成sql。

2、功能强大,数据库无关性好,O/R映射能力强,需要写的代码很少,开发速度很快。

3、有更好的二级缓存机制,可以使用第三方缓存。

4、数据库移植性良好。

5、hibernate拥有完整的日志系统,hibernate日志系统非常健全,涉及广泛,包括sql记录、关系异常、优化警告、缓存提示、脏数据警告等

Hibernate的缺点:

1、学习门槛高,精通门槛更高,程序员如何设计O/R映射,在性能和对象模型之间如何取得平衡,以及怎样用好Hibernate方面需要的经验和能力都很强才行

2、hibernate的sql很多都是自动生成的,无法直接维护sql;虽然有hql查询,但功能还是不及sql强大,见到报表等变态需求时,hql查询要虚,也就是说hql查询是有局限的;hibernate虽然也支持原生sql查询,但开发模式上却与orm不同,需要转换思维,因此使用上有些不方便。总之写sql的灵活度上hibernate不及mybatis。

Mybatis的优点:

1、易于上手和掌握,提供了数据库查询的自动对象绑定功能,而且延续了很好的SQL使用经验,对于没有那么高的对象模型要求的项目来说,相当完美。

2、sql写在xml里,便于统一管理和优化, 解除sql与程序代码的耦合。

3、提供映射标签,支持对象与数据库的orm字段关系映射

4、 提供对象关系映射标签,支持对象关系组建维护

5、提供xml标签,支持编写动态sql。

6、速度相对于Hibernate的速度较快

Mybatis的缺点:

1、关联表多时,字段多的时候,sql工作量很大。

2、sql依赖于数据库,导致数据库移植性差。

3、由于xml里标签id必须唯一,导致DAO中方法不支持方法重载。

4、对象关系映射标签和字段映射标签仅仅是对映射关系的描述,具体实现仍然依赖于sql。

5、DAO层过于简单,对象组装的工作量较大。

6、不支持级联更新、级联删除。

7、Mybatis的日志除了基本记录功能外,其它功能薄弱很多。

8、编写动态sql时,不方便调试,尤其逻辑复杂时。

9、提供的写动态sql的xml标签功能简单,编写动态sql仍然受限,且可读性低。

char 和 varchar 的区别是什么?

1.char类型的长度是固定的,varchar的长度是可变的。
这就表示,存储字符串’abc’,使用char(10),表示存储的字符将占10个字节(包括7个空字符)
使用varchar(10),则表示只占3个字节,10是最大值,当存储的字符小于10时,按照实际的长度存储。
2.char类型的效率比varchar的效率稍高
3.char与varchar的区别
varchar是oracle开发的一个数据类型。
工业标准的varchar可以存储空字符串,oracle的varchar还可以存储NULL值,如果想要有向后兼容的能力建议使用varchar
4.varchar比char节省空间,但是在效率上比char稍差些。既要获得效率即必须牺牲一点空间,这就是设计上的"“以空间换时间”"
varchar虽然比char节省空间,但是一个varchar列经常被修改,而且每次修改的数据长度不同,这会引起“行迁移的现象”,
而这造成的多余的I/O,是数据库设计中尽量避免的,在这种情况下使用char代替varchar会更好些。

float和double类型的区别如下:

1、变量类型不同

float属于单精度型浮点数据。

double属于双精度型浮点数据。

2、指数范围不同

float的指数范围为-127~128。

double而double的指数范围为-1023~1024

3、表达式指数位不同

float的表达式为1bit(符号位)+8bits(指数位)+23bits(尾数位)

double的表达式为1bit(符号位)+ 11bits(指数位)+ 52bits(尾数位)

4、占用内存空间不同

float占4个字节(32位)内存空间,其数值范围为3.4E-38~3.4E+38。

double占8 个字节(64位)内存空间,其数值范围为1.7E-308~1.7E+308。

5、有效位数不同

float只能提供七位有效数字。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值