Java基础
String、StringBuffer、StringBuilder:三者异同?
答:底层存储jdk9开始从char[]数组变成byte[]数组;
String
:不可变字符序列,声明为fianl,不可扩容,效率最低;因为每次生成对象都会对系统性能产生影响,特别当内存中无引用对象多了以后,JVM的GC就会开始工作,那速度是一定会相当慢的。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lWQUpT1j-1618844315047)(C:\Users\jin\AppData\Roaming\Typora\typora-user-images\image-20210318132256063.png)]
StringBuffer
:可变字符序列,可扩容,
线程安全
,效率低;
StringBuilder
(jdk5.0):可变字符序列,可扩容,
线程不安全
,效率高;
对象能够被多次的修改,并且
不产生新的未使用对象
;
类继承关系:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gPvxMXFh-1618844315051)(C:\Users\jin\AppData\Roaming\Typora\typora-user-images\image-20210318133046631.png)]
区别:
- 如果操作少量的数据使用String;
-
多线程
操作字符串缓冲区下操作大量数据
StringBuffer
; -
单线程
操作字符串缓冲区操作大量数据
StringBuilder
;
String初始化时可以赋空值;StringBuffer不行;
-
字符串拼接
时:String S1 = “This is only a” + ” simple” + ” test”;
StringBuffer Sb = new StringBuffer(“This is only a”).append(” simple”).append(” test”);String远大于StringBuffer,因为JVM将”This is only a” + ” simple” + ” test”看成“This is only a simple test”;
若换成String S1 = S2 +S3 + S4;则速度就没StringBuffer快了;
StringBuffer 上的主要操作是 append 和 insert 方法,可重载这些方法,以接受任意类型的数据。每个方法都能有效地将给定的数据转换成字符串,然后将该字符串的字符追加或插入到字符串缓冲区中。append 方法始终将这些字符添加到缓冲区的末端;而 insert 方法则在指定的点添加字符。
StringBuffer、StringBuilder初始化底层数组长度为:
字符串长度+16
;扩容机制:x2+2
== 和 equals()的区别?
equals()
:直接继承Object中的equals()方法时,比较的是两个引用是否指向的是同一个对象;
==
: 对于基本类型比较的是值,对于引用类型比较的是引用地址;
hashCode()和equals()的区别?
-
equals相等,hashcode不一定相等;
若equals方法未重写,则比较的是堆中地址,相等则是同一个对象,hashcode不管重写不重写都相等;
若equals方法重写,比较的是对象的成员变量值,equals相等有可能是队中new了一个对象,地址不相同;
-
hashcode相等,equals不一定相等;
若未重写,代表的是一个地址,那么就是同一个对象,equals不管是否重写都相等;
若重写,代表的是成员变量的hashcode值,但是像HashMap集合相同的hashcode值,其值不一定相同,因为它是数组+链表+红黑树的存储结构;
String str=“donkey” 与 String str=new String(“donkey”) 一样吗?
答:不一样;因为内存的分配方式不一样。前者,Java虚拟机将其分配到常量池中;后者,Java虚拟机将其分到堆内存中,在内存中开辟一块新的地址用来存放它。
String s = new String(“xyz”);创建几个String Object?
答:一个或两个;若常量池中存在”xyz“,只在new操作时,在堆中开辟了一个对象,引用常量池中的对象;若不存在,在“=”操作时,常量池中创建一个字符串对象,“new”操作时,堆中创建一个对象,引用该字符串对象;
final, finally, finalize的区别?
final
:常用来修饰属性,方法,类;属性不可更改,方法不可被覆盖,类不可被继承;
finally
:异常中的关键字,在方法中出现时,无论异常是否出现都会执行其代码块;
finalize
:是Java垃圾回收机制中的一个方法,重写其方法可回收其他资源,如文件关闭;
(JVM不保证此方法总被调用一个对象的finalize()方法只会被调用一次,而且finalize()被调用不意味着gc会立即回收该对象,所以有可能调用finalize()后,该对象又不需要被回收了,然后到了真正要被回收的时候,因为前面调用过一次,所以不会调用finalize(),产生问题。)
JVM运行原理
JVM编译
将Java问价经过一次编译后,Java代码编译成Java字节码文件(.class),然后可以在任意安装了对应的JVM平台上运行,这就是
一次编译,到处运行
附:https://www.cnblogs.com/whhjava/p/9916626.html
类加载机制
分为七步:加载(loading)-> 验证(Verification)-> 准备(Preparation)-> 解析(Resolution)-> 初始化 (Initialization)-> 使用(Using)-> 卸载(Unloading)
第一步:加载;获取二进制字节流(class文件);将静态的存储节后转换为方法区中的运行时数据结构;生成一个对象放入到Java堆中,做为对方法区的引用;
第二步:验证;class文件的表示:版本号、每个部分是否正确(字段表、方发表等)、验证常量池(常量类型、数据结构等是否正确,UTF-8是否正确),元数据验证(父类验证、继承验证、final验证),字节码验证,符号引用验证,若有错误出现,则验证失败;
第三步:准备;为类变量分配内存和设置类变量初始化。此过程,只针对static类变量进行内存分配,未赋值,所有的类变量都是初始化值;如果是final,会直接对应到常量池中,直接赋值;
第四步:解析;对符号引用进行解析,将符号引用解析为直接引用(指向目标的指针或偏移量),主要是类、接口、字段、方法等。
第五步:初始化;执行方法的过程,对静态代码块进行初始化,对类进行初始化;
第六步:使用;使用class文件
第七步:卸载;不在使用时,卸载class文件;
JDk JRE JVM :
JDK
是开发工具包,
JRE
是包括JVM,JRE有运行.class文件的java.exe;
JVM是Java虚拟机,JVM在执行字节码时,把字节码解释成具体平台的机器指令执行,”一次编译,到处运行“
JDK下的jre文件夹下有俩个文件夹lib和bin;在这里可以理解为bin就是jvm,lib就是类库;所以
JRE=JVM+类库
;
IO流
streams
是一组有顺序的,有起点和终点的字节集合,是数据传输的总称。数据传输
分类
:
数据流向:输入流、输出流
数据类型:字节流、字符流
字节流
:InputStream和OutputStream,每次读写一个字节,直接连接到输入源的流;
InputStream :
FileInputStream
BufferedInputStream
DataInputStream
ObjectInputStream
OutputStream:
FileOutputStream
BufferedOutputStream
DataOutputStream
ObjectOutputStream
字符流
:Reader和Writer以字符为单位进行数据处理的IO流。本质是居于字节流读取时,去查找指定的码表;
Reader:
InputStreamReader
FileReader
BufferedReader
Writer:
OutputStreamWriter
FileWriter
BufferedWriter
IO流模型:
BIO,NIO,AIO
同步
:用户进程触发IO操作并等待或轮询的去看IO操作是否就绪
异步
:用户进程触发IO操作以后便开始做自己的事情,当IO操作完成的时候会得到IO完成的通知;
阻塞
:所谓阻塞方式,当试图对该文件描述进行读写时,如果当时没有东西可读,或者暂时不可写,程序就进入等待状态
非阻塞
:如果没东西可读或不可写,读写函数马上返回,而不会等待
同步阻塞IO
(BIO):服务器实现模式为一个链接一个线程,即客户端有连接请求时服务器就启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销,当然可以通过线程池机制改善;
编程实现
:BIO是同步阻塞IO流,每个获取的连接都需要创建一个新的处理线程
同步非阻塞IO
(NIO):服务器实现模式为一个请求一个线程,即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有I/O请求时才启动一个线程进行处理。用户线程也需要不时的询问IO操作是否就绪,这就要求用户进程不停的去询问;
编程实现
:jdk1.4开始实现;
工作原理:
由一个专门的线程来处理所有的IO事件,并负责分发;
事件驱动机制:事件到的时候触发,而不是同步的去监视事件;
线程通讯:线程之间通过wait和notify等方式通讯。保证每次上下文切换都是有意义的。减少无谓的线程切换;
服务端和客户端都各自维护一个管理通道的对象(selector),该对象能检测一个或多个通道(channel)上的事件;
以服务器为例:如果服务端的selector上注册了读事件,某时刻客户端给服务端发送一些数据,阻塞IO这时会调用;
read()方法阻塞读取数据,而NIO的服务端会在selector中添加一个读事件。服务端的处理线程会轮询地访问selector;
如果访问selector时发现有感兴趣的事件到达,则会处理这些事件,如果没有则处理线程会一直阻塞直到感兴趣的事件到达;
异步阻塞IO
(AIO):用户进程只需要发起一个IO操作然后立即返回,等IO操作真正完成以后,应用程序会得到通知,此时用户进程只需要对数据进行处理就好,不需要进行实际的IO读写操作,因为真正的IO读写或者写入操作已由内核完成;
编程实现
:jdk1.7开始实现,实现了真正的异步非阻塞,将所有的IO都交给操作系统处理 ,当IO事件触发时,回调用户的Handle即可
面向对象
面向对象的三大重要特征:封装、继承、多态
面向对象:
把构成问题的事务分解成各个对象,建立对象的目的不是为了完成一个步骤,而是为了描述某个事务在整个解决问题的步骤中的行为。
声明周期
面向对象的分析:OOA
设计:OOD
编程:OOP
优点:耦合度低,扩展力强,更容易解决现实世界当中更复杂的业务逻辑;
缺点:性能比面向过程低;
封装
封装的某些信息隐藏在类内部,不允许外部直接访问,而是通过该类提供的方法来实现对隐藏信息的操作和访问;
封装步骤
:
1)、所有属性私有化,使用private修饰,只允许在本类中访问;
2)、提供get和set方法
ps:static修饰的方法调用:类名.方法名(实参)
没有static修饰的方法 调用:引用.方法名(实参)
public:访问在同一个class path下的所有类、接口;
protected:本包的类和子类;
default:本包的类;
private:本类内部;
构造方法
:
语法结构:
[修饰符列表]构造方法名(形参列表){
构造方法体
}
普通方法:
[修饰符列表] 返回值类型 方法名(形参列表){
方法体
}
1)、对于构造方法来说,构造方法名必须与类列保持一致;
2)、构造方法的作用:通过构造方法的调用,可以创建对象
3)、构造方法调用:
new 构造方法名(实参列表);
普通方法的调用:
方法修饰符中有static时,类名.方法名(实参列表);
方法修饰符中没有static时,引用.方法名(实参列表);
继承
从已有的类中派生新的类,新类拥有旧类的数据属性和行为,并能扩展新的能力。
语法结构:
B extnds A
ps:私有属性和方法不能被继承。
final:修饰类,不能被继承;修饰方法,不允许被重写;修饰属性,必须有值,初始化后不能被修改。
super:访问的是父类的属性和方法
this:保证了内存地址指向自身,this存储在JVM堆内存Java对象内部,每一个对象都有其对应的this。
子类可以拥有自己的属性和方法;可以用自己的方式实现父类的方法和属性(重写)
通过extends关键字实现类于类之间的继承;任何类都可以被用作父类,不管是否为抽象类;
一个父类被多个子类继承,但一个子类只能继承一个父类。与接口不同的是,可以实现多个接口。
多态
指允许不同类的对象对同一消息做出响应,即同一响应可以根据发送对象的不同而采用多种不同的行为方式;
实现多态称为动态绑定:指在执行期间判断所引用对象的实例类型,根据实际的类型调用其相应的方法。
作用:消除类型之间的耦合关系。
多态的必要条件:
1)、有继承;
2)、有方法重写
3)、父类引用指向子类对象
重载(overload)
同一个类中、方法名相同、参数列表不同:类型、顺序、个数;与返回值类型无关,与修饰符列表无关;
重写(override)
在子类与父类中,父类方法无法满足子类的业务需求;
返回值类型相同、方法名相同、形参列表相同;
访问权限不能低于父类、抛出的异常更少;
私有方法不能继承,不能重写;
构造方法不能继承,不能重写;
重写只发生在方法中,不针对属性。
抽象类
在普通类的结构里增加了抽象方法的组成部分;
抽象方法:没有方法体的方法,即没有{},同时还需要使用abstract修饰;
有抽象方法的类是抽象类,抽象类要使用abstract关键字声明;
抽象类不能直接实例化,当一个类实例化后,可以调用类中的属性,而抽象方法没有方法体,则无法进行调用。
抽象类使用原则:
1)、抽象方法必须为publish或protected,默认为publish
2)、抽象列不能直接实例化,需要依靠子类采用向上转型的方式处理;
3)、必须有子类
4)、子类必须覆写抽象类中的全部抽象方法,若没有实现父类的抽象方法,则子类也定义为abstract类。
类加载顺序
父类静态变量;
父类静态代码块;
子类静态变量;
子类静态代码块;
父类非静态变量;
父类构造函数;
子类非静态变量;
子类构造函数;
计算机网络
TCP三次握手
几个简单概念:
主机A向主机B发送数据的同时,主机A也可以接收主机B发过来的数据;
在TCP报文包里,有六个标志位 ,这里了介绍其中的两个:SYN包与ACK包;
SYN包:请求建立连接的数据包,SYN=1,则表示要建立连接;
ACK包:回应数据包(用来做回应的)表示接收到了对方的某个数据包,仅当ACK=1时确认号字段才有效;
seq序列号:用来标记数据包的顺序;
ack确认号:表示序号为确认号减去1的数据包及其以前的所有数据包已经正确接收,也就是说他相当于下一个准备接收的字节的序号 (如果ack确认号是101,则表示前100个都已经收到了);
当我们发一个SYN=1的包时,会得到一个ACK=1的包;
三次握手
:
第一次握手:建立连接时,客户端发送数据包,标志位SYN=1,随机seq=x到服务器(x代表随机生成的数)
第二次握手:服务器收到SYN=1的包,知道客户端要建立连接,返回SYN=1和ACK=1,ack=x+1,和随机seq=y (y代表随机生成的数)
第三次握手:客户端厚道服务器的SYN+ACK包,向服务器发送确认包ACK=1,ack=y+1
WEB
Get和Post
get把参数包含在URL中,POST通过request body传递参数
get比post更不安全,因为参数直接暴露在url上
get在浏览器回退时是无害的,而post会再次提交请求
GET在浏览器回退时是无害的,而POST会再次提交请求。
GET产生的URL地址可以被Bookmark,而POST不可以。
GET请求会被浏览器主动cache,而POST不会,除非手动设置。
GET请求只能进行url编码,而POST支持多种编码方式。
GET请求参数会被完整保留在浏览器历史记录里,而POST中的参数不会被保留。
GET请求在URL中传送的参数是有长度限制的,而POST么有。
对参数的数据类型,GET只接受ASCII字符,而POST没有限制。
传文件、form表单等必须是post
Session与Cookie
session存在服务器里,客户端不知道其中的信息;cookie存在客户端,服务器能够知道其中的信息
session中保存的是对象,cookie里保存的是字符串;
session不能区分路径,同一个用户在访问同一个网站期间,所有的session在任何一个地方都可以读取到; 而cookie如果设置了路径参数,那么同一个网站中不同路径下的cookie是互相访问不到的;
session需要借助cookie才能正常生效,如果客户端禁止cookie的话,session将失效;
类与接口
类是单继承,多实现;接口不能实现另一个接口,只能继承该接口,且可以多继承。
Split()
String str = "";
System.out.println(str.split(",").length);
解释:
length()
方法是用在
String
类型上获得其长度;
length
方法是用在**String[]**类型上求其长度;
多线程
状态
运行、就绪、挂起、结束
进程和线程的关系
进程
是一段正在执行的程序;
线程
是程序执行过程中,能够执行程序代码的一个执行单元;
线程可称为轻量级进程,是程序执行的最小单元;
一个进程可以有多个线程,各个线程之间共享程序的内存空间(代码段、数据段、堆空间)以及一些进程级的资源,但也拥有属于自己的堆空间;
为什么要使用
多线程
:
- 使用多线程可以减少程序的响应时间;
- 与进程相比,线程的创建和切换开销较小;
- 使用多线程能简化程序的结构;
解释:
length()
方法是用在
String
类型上获得其长度;
length
方法是用在String[]类型上求其长度;
多线程
状态
运行、就绪、挂起、结束
进程和线程的关系
进程
是一段正在执行的程序;
线程
是程序执行过程中,能够执行程序代码的一个执行单元;
线程可称为轻量级进程,是程序执行的最小单元;
一个进程可以有多个线程,各个线程之间共享程序的内存空间(代码段、数据段、堆空间)以及一些进程级的资源,但也拥有属于自己的堆空间;
为什么要使用
多线程
:
- 使用多线程可以减少程序的响应时间;
- 与进程相比,线程的创建和切换开销较小;
- 使用多线程能简化程序的结构;