Java基础

一 编程入门

总线:在个人计算机上,总线搭建在主板上,主板是一个连接计算机各个部分的电路板。
冯诺依曼体系结构:输入设备(输入数据和程序)、存储器(内存和硬盘)、输出设备(输出处理结果)、运算器(完成数据加工处理)、控制器(控制程序执行)。cpu包括运算器和控制器。
处理器:处理器型号AMD Ryzen 5 5600H with Radeon Graphics 3.30 GHz
AMD代表品牌5代表第5代 5后面的数字越大越好H代表高电压变压器比较大 U代表低电压薄本
GHZ代表每秒钟的脉冲次数,即处理指令的频率,越大越好。
1KHZ = 1024HZ 1MHZ= 1024KHZ 1GHZ = 1024MHZ
内存:一个0或者一个1存储为一个比特(bit),是计算机中最小的存储单位。
计算机中最基本的存储单位是字节(byte)。每个字节由8个比特构成。
计算机的存储能力是以字节的多少来衡量的。如下:
千字节(Kilobyte,KB)= 1024B
兆字节(megabyte,MB)= 1024KB
千兆字节(gigabyte,GB)= 1024MB
万亿字节(terabyte,TB)= 1024GB
内存(也叫Random-Access Memory,RAM):由一个有序的字节序列组成,用于存储程序及程序需要的数据。一个程序和它的数据在被CPU执行前必须移到计算机的内存中。
每个字节都有一个唯一的地址,使用这个地址确定字节的位置,以便于存储和获取数据。
显示器:像素密度等于长度像素数的平方 加 宽度像素数的平方 再开方 再除以屏幕尺寸
LCD是发光源发光 LED是自发光源
像素:像素密度=√[(长度像素数)2+(宽度像素数)2]/屏幕尺寸
操作系统:上接应用程序,下接硬件。
万维网:互联网包括因特网,因特网包括万维网。软件架构分B/S C/S 两种
IT定律之计算机行业发展规律:摩尔定律 安迪-比尔定律 反摩尔定律
二进制数据的存储方式:所有的数值,不管正负,底层都以补码的方式存储。
计算机标识是IP,软件标识是端口号
迅雷下载速度快是因为多线程下载。
cpu寄存器比内存处理快原因一:距离不同距离不是主要因素,但是最好懂,
所以放在最前面说。原因二:硬件设计不同。原因三:工作方式不同。
操作系统的删除是逻辑删除,非物理删除(磁盘数据可恢复)。
字符在磁盘中占用的字节数比内存中要大。
运行框输入mspaint可打开画图。

二 Java语言概述

软件开发:软件,即一系列按照特定顺序组织的计算机数据和指令的集合。有系统软件和应用软件之分。人机交互方式:图形化界面GUI 命令行方式CLI。
DOS(Disk Operating System,磁盘操作系统)是Microsoft公司在Windows之前推出的一个操作系统,是单用户、单任务(同一时间只能执行一个任务)的操作系统。现在被Windows系统取代。
编译程序(把高级语言程序翻译成机器语言程序)属于系统软件。
cmd是Command (Prompt)的缩写 dir是directory的缩写
文档注释内容可以被JDK提供的工具javadoc所解析,生成一套以网页文件形式体现的该程序的说明文档。
API文档在线看https://docs.oracle.com/en/java/javase/17/docs/api/index.html
离线下载https://www.oracle.com/java/technologies/javase-jdk17-doc-downloads.html
.java源文件编译以后生成.class字节码文件然后使用java.exe指令进行解释运行,一旦解释运行了就要使用内存的资源执行此程序了,其实这个时候帮我们去做整个程序内存的分配和执行的是JVM
不同操作系统的JVM不一样,JDK也不一样。
Java语法过于复杂严谨,一般适用于大型网站开发整个架构会比较重,并非适用于所有领域比如Objective C、Swift在ios设备上有着无可取代的地位,浏览器中的处理几乎完全由JavaScript掌控,Windows程序通常都用C++或C#编写。Java在服务器端编程和跨平台客户端应用领域则很有优势。
JVM的功能:实现Java程序的跨平台性、自动内存管理(内存分配、内存回收)。
内存泄漏会导致内存溢出。
TIOBE是流行编程语言排行 www.tiobe.com
运行效率C大于Java大于Python 开发效率Python大于Java大于C
安装JdK时提示再单独安装一个Jre 有的电脑的操作系统的eclipse要识别单独的jre 所以也可以装。
G1是最受欢迎的GC算法。Java11及更高版本的G1收集器是默认的GC,而Java中并不是。出于对G1的喜爱,很多开发者才会选择抛弃Java8。
LTS是Long Term Support长期支持版本。
springboot3.0是需要用java17和spring6.0为基础建设。如果从企业选型最新springboot3.0作为架构来说,它搭配jdk17肯定是标配了。
配置环境变量path: windows系统执行命令时要搜索的路径。
配置JAVA_HOME是因为tomcat要识别JAVA_HOME
Hello world程序全都写对了还是报错找不到或无法加载主类是因为配置了Classpath它会在默认的路径中去不在当前路径找了。把Classpath删除掉就行了。
一个java源文件中被public修饰的类的类名必须与源文件名相同,即可以声明多个class但是只能最多有一个类声明为public的。
javac.exe用于编译 java.exe用于运行 javadoc.exe生成网页版文档。
应用程序 = 算法 + 数据结构
Java语言的特点:面向对象
健壮性:去除了C语言中的指针 自动的垃圾回收机制 ——>仍然会出现内存溢出内存泄漏
跨平台性:write once run anywhere 功劳归功于Jvm
关键字官方网址https://docs.oracle.com/javase/tutorial/java/nutsandbolts/_keywords.html
Java保留字有 goto const
在JAVA编程中,对象是最基本的单元
JAVA程序经编译后会产生byte code
为啥Java是半编译半运行:是字节码文件在运行期间分为编译器编译一部分代码,解释器解释运行一部分代码。

三 Java的基础语法

打印语句中使用\n这个转义字符来换行。
标识符:命名规范:java采用Unicode字符集,因此标识符也可以使用汉字说明,但是不建议使用。
变量是内存中的一个存储区域,该区域的数据可以在同一类型范围内不断被变化。
声明long型变量,必须以"l"或"L"结尾。 通常,定义整形变量时,使用int型。
定义float类型变量时,变量要以"f"或"F"结尾。
通常定义浮点型变量时,使用double型,因为范围大精度高省写f
字符型:char (1字符=2字节) String属于引用数据类型。
char、short、byte参与运算结果默认是int
计算机的底层是以补码的方式存储数据的。
后加加后减减是先赋值再运算,前加加前减减是先运算再赋值。
获取区间[a,b]之间的随机整数:(int)(Math.random())*(b-a+1)+a
十进制转二进制:除2的余的逆
boolean不参与基本数据类型的运算。
Java语言的特点社区繁华活跃。
取模以后结果与被模数的符号一致。
引用数据类型包含类接口数组等,引用数据类型的大小统一为4个字节,记录的是其引用对象的地址。
流程控制
流程控制结构包括顺序,分支,循环。
for( ; ; ){} while(true){}
return是结束方法的。并非是专门来结束循环的。当一个方法执行到一个return语句时这个方法将被结束。
与break和continue不同的是,return直接结束整个方法,不管这个return处于多少个循环之内。
switch语句中的变量类型可以是byte short int char String
递归死循环会报Stack overflow异常。
数组
数组名是个引用类型的变量,它保存着的是数组的地址,不是数组中的数据。
数据结构:
数据与数据之间的逻辑关系:集合 一对一 一对多 多对多
数据的存储结构:线性表(比如顺序表数组)、链表、栈、队列。树形结构:二叉树。
图形结构:主要研究形状和图形数据元素之间的关系,
图形数据结构与一般 数据结构 不同,它必须要反映数据所对应元素之间的几何关系和拓扑关系。
二分法查询数组中元素的下标时要先对原数组排序
常量定义时必须赋值否则报错
变量本质上就是代表一个“可操作的存储空间”,空间位置是确定的,但是里面放置什么值不确定。局部变量在使用前需要初始化。
成员变量float的默认值是0.0 double的默认值是0.0 char的默认值是’\u0000’取值范围是0到65535
boolean的默认值是False
boolean占几个字节:编译时不谈占几个字节。但是JVM在给boolean类型分配内存空间时boolean类型的变量占据一个槽位(slot,等于4个字节)。
在内存中,byte\short\char\int\float\boolean\引用类型的变量:占用1个slot
double\long:占用2个slot
为啥Java中0.1+0.2结果不是0.3?答:它们采用了IEEE 754标准。IEEE是指“电气与电子工程师协会”,其在1985年发布了一个IEEE754计算标准:小数的二进制表达能够有最大的精度上限提升但无论如何物理边界是突破不了的,它仍然不能实现“每一个十进制小数都对应一个二进制小数”。
同样占4个字节为啥float比int的范围大:int类型第1位是符号位代表正负剩下的31位表示数值位。
float类型第一位是符号位代表正负余下的是8位指数位和23位底数位(底数是无符号的)。
虽然float比 int存储范围大但是精度不足。
一个double八个字节,一个字节八个bit,即一个double的空间是64位。
byte占一个字节,一个字节占8位,一位有两种状态,所以byte可表达(2的8次方等于)256个信息。又第一位表示负号,所以是-128到+127。

四 面向对象

内存解析是在运行时才涉及到
编译完源码以后生成一个或多个字节码文件
我们使用JVM中的类的加载器和解释器对生成的字节码文件进行解释运行。
意味着,需要将字节码文件对应的类加载到内存中,涉及到内存解析。
内存解析的说明:引用类型的变量,只可能存储两类值:null 或 地址值(含变量的类型)
1 方法的重载的细节说明:跟方法的权限修饰符、返回值类型、形参变量名、方法体都没有关系。
2 在通过对象调用方法时,如何确定某一个指定的方法:方法名—> 参数列表。
正因为有了这个问题才出现了方法的重载这么一说才定义了方法的重载。这样重载就变得合情又合理了。并不是我非得强调做这样这样的规定。
3 JavaSE 5.0中提供了Varargs(variable number of arguments)机制
,允许直接定义和多个实参相匹配的形参。
4 关于变量的赋值
如果变量是基本数据类型,此时赋值的是变量所保存的数据值。
如果变量是引用数据类型,此时赋值的是变量所保存的数据的地址值(含变量的数据类型)。
5 局部变量在栈,成员变量在堆(非static情况下)
方法内方法形参构造器内构造器形参代码块内的是局部变量,堆中new出来的结构中的是成员变量。
6 匿名对象只能用一次说的是匿名对象作为实参传递给形参时,
当然形参接收后是相当于有个名字的是可以多次调用的。
7 高内聚:类的内部数据操作细节自己完成,不允许外部干涉 低耦合:仅对外暴露少量的方法用于使用。
8 封装性的体现:属性私有时提供setget,不对外暴露的私有的方法,单例
模式中的私有构造器,如果不希望类在包外被调用可以将类设置为缺省
的。权限修饰的体现是封装,没有权限修饰就不存在封装。
9 当形参和类的属性名同名时用this来区分,this可用来修饰属性、方法、构造器。
理解:当前对象或正在创建的对象。
10 this调用构造器
1)我们在类的构造器中可以显式的使用"this(形参列表)“方式调用本类中指定的其他构造器
2)构造器中不能通过"this(形参列表)“方式调用自己
3)如果一个类中有n个构造器,则最多有n-1个构造器中使用了"this(形参列表)”
4)规定:“this(形参列表)“必须声明在当前构造器的首行
5)构造器内部,最多只能声明一个"this(形参列表)”,用来调用其他的构造器。
11 类一定有构造器,这句话自始至终都是对的。
类的三要素:属性、方法、构造方法(方法名与类名相同有参覆盖无参)。
12 只要造对象就要有构造器,这句话自始至终都是对的。
13 默认的构造器的权限修饰符随类
14 子类重写的方法的权限修饰符不小于父类的被重写的方法的权限修饰符
如果父类被重写的方法的返回值类型是void则子类重写的方法的返回值类型只能是void
如果父类被重写的方法的返回值类型是A类型则子类重写的方法的返回值类型可以是A类或A类的子类
强调:如果父类被重写的方法的返回值类型是基本数据类型(比如double)
则子类重写的方法的返回值类型必须是相同的基本数据类型(必须是double)
子类重写的方法抛出的异常类型不大于父类被重写的方法的异常类型
子类和父类中的同名同参数的方法要么都声明为非static的(考虑重写),要么都声明为static的(不是重
写),是因为静态方法不能被覆盖静态方法随着类的加载而加载
15 子类对象实例化的全过程之从结果上看因为继承所以就获取了父类中声明的属性或方法,
从过程上看是因为创建子类对象时一定会直接或间接的调用其父类的构造器一直到Object类中空参的构
造器为止正因为这样加载过父类的结构所以看到内存中有父类的结构子类对象才可以进行调用。
明确:虽然调用了父类的构造器但就创建了一个对象即new的那个子类的。
堆中对象是地址值
堆中成员方法是一个地址值,指向方法区(Method Area)
16 多态性的使用前提是 类的继承关系 和 方法的重写
对象的多态性只适用于方法(编译看左边运行看右边)不适用于 属性(编译和运行都看左边)
17 区分方法的重写和重载?
二者概念
具体规则
重写表现为多态性,重载不表现为多态性
Println()能输出任何东西,是一个标准的重载方法。判断一个类中的两个方法是否相同当然是看方法名和参数列表同时相同。
18 多态性的应用比如方法内形参是父类实参可是父也可是子类,
再比如形参Object类实参随意
再比如形参是Connection实参可是各种数据库连接
19 面向对象的三大主线:类和类的成员 三大特征 关键字的使用
20 双等号不能比较没有父子关系的两个对象(语法上会报错双等号cannot be applied to 两个对象)
20 **equals()**方法的使用
是一个方法而非运算符 只能适用于引用数据类型
Object类中equals()的定义是public boolean equals(Object obj){ return (this == obj); }
说明Object类中定义的equals()和==的作用是相同的即比较两个对象的地址值是否相同
像String、Date、File、包装类等都重写了Object类中的equals ()方法
重写以后比较的是两个对象的实体内容是否相同。
通常我们自定义的类如果使用equals()的话也是想比较两个对像的实体内容
所以需要对Object类中的equals()进行重写
21 **Object类中的toString()**的使用:
当我们输出一个自定义对象的引用时实际上是调用它的 toString()
Object类中toString()的定义是类名+”@”+转成十六进制的hashCode的值
像String、Date、File、包装类等都重写了Object的toString()返回"实体内容"信息
22 三元运算符 类型要统一(会类型自动提升)
23 抽象类、接口的使用肯定体现了多态性。(抽象类、接口不能实例化)
24 多态性是运行时行为。编译时不知道,等运行时才知道。证明过程点这里
25 单元测试方法的权限是public 没返回值 没形参 类是public的 要提供公共的无参的构造器
26 静态代码块:内部可以输出语句
随着类的加载而执行而且只执行一次
其作用是初始化类的信息
如果一个类中定义了多个静态代码块则按照声明的先后顺序执行
静态代码块的执行要先于非静态代码块
静态代码块内只能调用静态的属性静态的方法不能调非静态的结构
27 非静态代码块:内部可以输出语句
随着对象的创建而执行
每创建一个对象就执行一次非静态代码块
其作用是可以在创建对象时对对象的属性等进行初始化
如果一个类中定义了多个非静态代码块则按照声明顺序
非静态代码块内可以调用静态的属性静态的方法或非静态属性非静态方法
28 实例化子类对象时涉及到父类、子类中静态代码块、非静态代码块、构造器的加载顺序:
由父及子,静态先行。
29 属性的赋值顺序:默认初始化
显式初始化/在代码块中赋值
构造器中初始化
创建了对象以后可通过"对象.属性"的方式进行赋值
30 final用来修饰一个类表示此类不能被其他类所继承比如String System StringBuffer
final用来修饰方法表明此方法不可以被重写比如Object类中的getClass()
31 static final用来修饰属性:全局常量(接口中属性全是)
32 匿名子类的匿名对象的写法:重写父类的抽象方法直接传给形参
33 模板方法设计模式是编程中经常用到的模式。各个框架、类库中都有他的影子比如常见的有:
数据库访问的封装 Junit单元测试 JavaWeb的Servlet中关于doGet/doPost方法调用
Hibernate中模板程序 Spring中JDBCTemlate、HibernateTemplate等
34 静态资源被全局对象共享,只有一份。
35 继承:子类继承父类相当于把父类的功能复制了一份但构造方法不能被继承
继承后子类可以使用父类的所有非私有资源,私有资源不可用的原因是不可见。
36 接口在jdk8之前只能有抽象方法,接口中声明的属性默认使用public static final修饰。
37 抽象类的意义:不明确方法要实现什么就让子类去明确下(很多个子类都实现了,那就规定子类必须来实现抽象类的方法吧)。
38 没有多态也就不会有抽象类不会有接口。
39 静态变量与实例变量的区别:静态变量jdk6及之前存放在方法区,jdk7及之后存放在堆空间,随着类的加载而加载,由于类只会加载一次所以静态变量也只有一份,可以被类直接调用,也可以用对象调用,随着类的卸载而消亡。
实例变量存放在堆空间的对象实体中,随着对象的创建而加载,每个对象拥有一份实例变量,只能使用对象进行调用,随着对象的消亡而消亡。
40 静态方法不能被重写,不能被覆盖,静态方法不存在多态。(子父类中同名方法要么都是static方法,要么都不是static)
41 向下转型可能会出现ClassCastException编译能通过运行时会报错,建议在向下转型之前,使用instanceOf进行判断,避免出现类型转换异常。
42 面向对象编程的本质是:以类的方式组织代码,以对象的方式组织(封装)数据。
从认识论角度考虑是先有对象后有类。对象是具体的事物,类是抽象的,是对对象的抽象。
从代码运行角度考虑是先有类后有对象,类是对象的模板。

五 多线程

1 线程名的格式是Thread-0 或 Thread-1 拼接api和变量时可在中间添加符号(进行区分)
2 程序是一段静态的代码
3 进程是正在运行的一个程序
4 线程是程序内部的一条执行路径。作为调度和执行的单位,
每个线程拥有独立的运行栈和程序计数器(pc),线程切换的开销小,
多个线程共享同一个进程中的结构:方法区、堆
5 单核CPU其实是一种假的多线程
一个Java应用程序java.exe,其实至少有3个线程:
main()主线程,gc()垃圾回收线程,异常处理线程。当然如果发生异,会影响主线程。
6 并行:多个CPU同时执行多个任务
并发:一个CPU(采用时间片)同时执行多个任务。比如秒杀
7 Java中的线程分两类一种是守护一种是用户线程
唯一的区别是判断JVM何时离开,thread.setDaemon(true)把一个用户线程变成守护线程
Java垃圾回收是一个典型的守护线程,若JVM中都是守护线程,当前JVM将退出。
8 线程的生命周期:新建 就绪 阻塞 运行 死亡
9 新建一个线程在Java中默认占一兆操作系统空间内存
10 线程创建的方式:

六 Java常用类

1 >util.Date —>sql.Date利用getTime()
2 String s = new String(“hello”)在内存中创建了两个对象一个在堆中一个在常量池,
栈中变量名指向堆,堆中的value指向常量池。String不能被继承因为声明为finnal啦
3 如何理解String的不可变性:String指向常量池的数据,对于数据我们不能做任何修改,数据不可变。
进行修改需新造数据存储新的内容
4 不涉及多线程或涉及到了但StringBuilder不是共享数据时StringBuilder可使用
5 String:字符串,使用一对""引起来表示
String声明为final的,不可被继承
String实现了Serializable接口表示字符串是支持序列化的
实现了Comparable接口表示String可以比较大小
String内部定义了final char[] value用于存储字符串数据
通过字面量的方式(区别于new给一个字符串赋值,此时的字符串值声明在字符常量池中
字符串常量池中是不会存储相同内容(使用String类的equals()比较,返回true)的字符串的。
7 String -->StringBuffer、StringBuilder:调用其构造器
StringBuffer、StringBuilder–>String:方法一调用String构造器
方法二StringBuffer、StringBuilder的toString()
8 JVM中字符串常量池存放位置说明:
jdk 1.6 (jdk6.0 java6.0):字符串常量池存储在方法区(永久区)
jdk 1.7:字符串常量池存储在堆空间
jdk 1.8:字符串常量池存储在方法区(元空间)
9 静态方法不能重写

七 枚举类&注解

1 Serializable是一个标识接口
2 自定义注解必须配上注解的信息处理流程(使用反射)才有意义。
3 对现有的数据进行修饰的数据叫元数据

八 Java集合

1 数组的缺点:长度不可修改
提供的方法有限对于添加删除插入非常不便效率不高
获取数组中实际元素的个数没有现成的属性或方法可用
存储的数据有序可重复对于无序不可重复的需求不能满足
2 contains(Object obj):判断当前集合中是否包含obj,我们在判断时会调用obj对象所在类的equals()
3 数组转换成集合之后的类型是List合情合理,因为list有序可重复,数组有序可重复
List arr2 = Arrays.asList(new Integer[]{123,456});注意类型要写成包装类,
要是写成int它会识别为一个元素
4 两种基本的数据结构 顺序表 链表
5 涉及到线程安全且共享数据是ArrayList也不用vector,用collections工具类。
6 HashSet底层实现:所有的key指向同一个value就不用比较value,就相当于没value。
7 向Collection接口的实现类的对象中添加数据obj时,要求obj所在类要重写equals()
(不重写的话会调父类的equals这样就不合适了)
8 集合元素的遍历操作,使用迭代器Iterator接口
内部方法:hasNext()和next()
集合对象每次调用iterator()方法都得到一个全新的迭代器对象,默认游标都在集合的第一个元素之前
内部定义了remove(),可以在遍历的时候,删除集合中的元素。此方法不同于集合直接调用remove()
注意:如果还未调用next()或在上一次调用next方法之后已经调用了remove方法,再调用remove都会报
IllegalStateException
9 ArrayList的源码分析 jdk7情况下
ArrayList list = new ArrayList();//底层创建了长度是10的Object[]数组elementData
list.add(123);//elementData[0] = new Integer(123);
… list.add(11);//如果依次的添加导致底层elementData数组容量不够,则扩容。
默认情况下,扩容为原来的容量的1.5倍,同时需要将原有数组中的数据复制到新的数组中。
(类似于(线程不安全的)StringBuilder)
结论:建议开发中使用带参的构造器:ArrayList list = new ArrayList(int initialCapacity) 指定合适的容量
避免扩容
jdk 8中ArrayList的变化:
ArrayList list = new ArrayList();//底层Object[] elementData初始化为{},并没有创建长度为10的数组,
(源码注释还是Constructs an empty list with an initial capacity of ten)
list.add(123);//第一次调用add()时,底层才创建了长度10的数组,并将数据123添加到elementData[0]
… 后续的添加和扩容操作与jdk 7无异
结论:jdk7中的ArrayList的创建类似于单例的饿汉式,而jdk8中的ArrayList的对象的创建类似于
单例的懒汉式,延迟了数组的创建,节省内存。
10 LinkedList的源码分析
LinkedList list = new LinkedList();内部声明了Node类型的first和last属性,默认值为null
list.add(123);//将123封装到Node中,创建了Node对象。
其中,Node定义为:
private static class Node{
E item;
Node next;
Node prev;
Node(Node prev,E element, Node next){
this.item = element;
this.next = next;
this.prev = prev;
} }
体现了LinkedList的双向链表的说法 (不涉及扩容很灵活愿加几个加几个)
11 Vector的源码分析:jdk7和jdk8中通过Vector()构造器创建对象时,底层都创建了长度为10的数组
在扩容方面,默认扩容为原来数组长度的2倍
12 Set接口中没有额外定义新的方法,使用的都是Collection中声明过的方法
要求:向Set中添加的数据,其所在类一定要重写hashCode()和equals()
要求:重写的hashCode()和equals()尽可能保持一致性:相等的对象必须具有相等的散列码
重写两个方法的小技巧:对象中用作equals()方法比较的Field,都应该用来计算hashCode值
13 Set:存储无序的、不可重复的数据
无序性:不等于随机性。
存储的数据在底层数组中并非按照数组索引的顺序添加,而是根据数据的哈希值决定的。
不可重复性:保证添加的元素按照equals()判断时,不能返回true。即:相同的元素只能添加一个。
14 添加元素的过程;以HashSet为例:
我们向HashSet中添加元素a,首先调用元素a所在的类的hashCode()方法,计算元素a的哈希值,
此哈希值接着通过某种算法(散列函数)计算出在HashSet底层数组中的存放位置
(即为:索引位置),判断数组此位置上是否已经有元素:
如果此位置上没有其他元素,则元素a添加成功。
如果此位置上有其他元素b(或以链表形式存在的多个元素),则比较元素a与已有元素的哈希值:
如果不同就添加成功新元素7上8下;
如果哈希值相同就继续比较equals(比较格式为a.equals(已有元素)):
如果equals返回false就添加成功,返回true就添加失败。
HashSet底层:数组+链表的结构
15 LinkedHashSet作为HashSet的子类,在添加数据的同时每个数据还维护了两个引用
记录此数据前一个数据和后一个数据
优点:对于频繁的遍历操作,LinkedHashSet效率高于HashSet
16 Map中的key:无序的、不可重复的,使用Set存储所有的key —>要求key所在的类重写equals()和
hashCode() (以Hash Map为例)
Map中的value:无序的、可重复的,使用Collection存储所有的value —>value所在的类要重写
equals()
一个键值对:key-value构成了一个Entry对象
Map中的entry:无序的、不可重复的,使用Set存储所有的entry
Map没有自己的迭代器,所以迭代时通常需要转成set集合来迭代
17 HashMap的底层实现原理?以jdk7为例
HashMap map = new HashMap():在实例化以后,底层创建了长度是16的一维数组Entry[] table。
…可能已经执行过多次put…
map.put(key1,value1):
首先,调用key1所在类的hashCode()计算key1哈希值,此哈希值经过某种算法计算以后得到在Entry数
组中的存放位置。如果此位置上数据为空,此时的key1-value1添加成功。 ----情况1
如果此位置上的数据不为空(此位置存在一个或多个以链表形式存在的数据),
比较key1和已经存在的数据的哈希值:
如果key1的哈希值与已经存在的数据的哈希值不相同则key1-value1添加成功 ----情况2
如果key1的哈希值与已经存在的某个数据(key2-value2)的哈希值相同则调用key1所在类的equals(key2):
如果equals()返回false:则key1-value1添加成功 ----情况3
如果equals()返回true:则使用value1替换value2(此时的put()就起到了修改的作用)
补充:关于情况2和情况3:此时key1-value1和原来的数据以链表的方式存储。
在不断的添加过程中,会涉及到扩容问题,当超出临界值(且要存放的位置非空)默认扩容为原来容量
的2倍,并将原有的数据复制过来。
jdk8 相较于jdk7在底层实现方面的不同:
(1). new HashMap():底层没有创建一个长度为16的数组
(2). jdk 8底层的数组是 Node[],而非Entry[]
(3). 首次调用put()方法时,底层创建长度为16的数组
(4). jdk7底层结构只有数组加链表,jdk8中底层结构数组+链表+红黑树
当数组的某一索引位置上的元素以链表形式存在的数据个数>8且当前数组的长度 > 64时,
此索引位置上的所有数据改为使用红黑树存储。
DEFAULT_INITIAL_CAPACITY : HashMap的默认容量16
DEFAULT_LOAD_FACTOR:HashMap的默认加载因子 0.75 (12的时候扩容比满了再扩链表少,
加载因子太小数组利用率就低了太大链表就多了,0.75可两者兼顾)
threshold:扩容的临界值=容量*加载因子=12
TREEIFY_THRESHOLD:Bucket中链表长度大于该默认值,转化为红黑树:8
MIN_TREEIFY_CAPACITY:桶中的Node被树化时最小的hash表容量:64
18 LinkedHashMap的底层实现原理(了解)
源码中:
static class Entry<K,V> extends HashMap.Node<K,V>{
Entry<K,V> before,after;//能够记录添加的元素的先后顺序
Entry(int hash, K key, V value, Node<K,V> next){
super(hash,key,value,next);
} }
19 向TreeMap中添加key-value 要求key必须是由同一个类创建的对象
因为要按照key进行排序 自然排序 定制排序 (不能按照value排)

九 泛型

静态方法中不能使用类的泛型因为泛型用于构造器而静态早于对象的创建。
异常类不能是泛型的。

十 File类与IO流

1 用Windows记事本打开能读的懂的文件就是纯文本文件,用字符流操作。
2 既然字节流可以操作所有文件,那么为什么还要学习字符流?
如果利用字节流把文本文件中的中文读取到内存中,有可能出现乱码。
如果利用字节流把中文写到文本文件中,也有可能出现乱码。
3 为什么字节流读取文本文件,可能会出现乱码?
因为字节流一次读一个字节,而不管GBK还是UTF-8一个中文都是多个字节,用字节流每次只能读其中的一部分,所以就会出现乱码。
6 想要进行拷贝,一律使用字节流或者字节缓冲流。
7 想要把文本文件中的数据读到内存中请使用字符输入流。想要把内存中的数据写入到文本文件中请使用字符输出流

十一 网络编程

1 网络通信三要素:IP(Internet Protocol Address)地址、端口号、规范通信协议。
2 IP地址分类方式一IPv4(占用4个字节)和 IPv6(占用16个字节)
分类方式二公网地址(万维网使用)和私有地址(局域网使用以192.168开头)
3 本地回路地址:127.0.0.1

十二 Java反射机制

1 每一个Java Class文件都是以0x CAFEBABE开头的。
2 Jdk17以后核心源码不能暴力反射
3 如何保证反射无法破坏单例:使用枚举类,因为使用反射调枚举类的构造器时报错,那咋办:只能使用现成的对象。
4 双亲委派机制能保证数据安全,不得被替换比如自定义的lang包的String类
5 用户自定义类的加载器的作用:实现应用的隔离(同一个类在一个应用程序中可以加载多份);数据的加密
6 使用类的加载器获取流并读取配置文件时,读取的文件的默认的路径为当前module下的src下。
new FileInputStream的方式的默认路径为当前的module
7 newInstance():调用此方法,创建对应的运行时类的对象。内部调用了运行时类的空参的构造器
要想此方法正常的创建运行时类的对象,要求:
1.运行时类必须提供空参的构造器
2.空参的构造器的访问权限得够。通常设置为public
8 在javabean中要求提供一个public的空参构造器。原因:
1.便于通过反射创建运行时类的对象
2.便于子类继承此运行时类时,默认调用super()时,保证父类有此构造器
9 Class的理解:针对于编写好的.java源文件进行编译会生成一个或多个.class字节码文件。接着,我们需要将.class字节码文件加载到内存中的方法区再使用java.exe命令对指定的.class文件进行解释运行吗。加载到内存中的.class文件对应的结构即为Class的一个实例。
10 获取Class对象的三种方式:类名.class、对象.getClass、Class.forName(类的全路径) 。实例化方式:Class对象.getDeclaredConstructor().newInstance()
11

十三 异常

1 sleep()异常只能try{}catch(){},是想让停止线程是一种可控行为。
2 有时候接口中抽象方法处加有异常,是为了让实现类中的方法有得抛。
3 异常处理的两种方式的选择:
前提:对于异常,使用相应的处理方式。此时的异常,主要指的是编译时异常。

  • 如果程序代码中,涉及到资源的调用(流、数据库连接、网络连接等),则必须考虑使用try-catch-finally来处理,保证不出现内存泄漏。
  • 如果父类被重写的方法没有throws异常类型,则子类重写的方法中如果出现异常,只能考虑使用try-catch-finally进行处理,不能throws。
  • 开发中,方法a中依次调用了方法b,c,d等方法,方法b,c,d之间是递进关系。此时,如果方法b,c,d中有异常,我们通常选择使用throws,而方法a中通常选择使用try-catch-finally。

4 两个关键字的区别:
throws使用在方法的声明处把产生的异常对象继续向上一层抛出,属于异常处理的一种方式。
throw使用在方法内部后边跟着的是异常对象表示手动抛出一个指定异常类的对象。
使用场景:throw用来产生异常对象,throws针对已产生的异常如何去处理,“上游排污下游治污”
5 throw后的代码不能被执行,编译不通过。
6 常见的Error有StackOverflowError、OutofMemoryError
常见的运行时异常(非受检异常)有:ArrayIndexOutOfBoundsException、NullPointerException、ClassCastException、NumberFormatException、InputMismatchException、ArithmeticException
常见的编译时异常:ClassNotFoundException、FileNotFoundException、IOException
7 try-catch使用细节:如果声明了多个catch结构,不同的异常类型在不存在子父类关系的情况下,谁声明在上在下都可以。如果多个异常类型满足子父类的关系,则必须将子类声明在父类结构的上面。否则,报错。
8 Throwable类是Java程序执行过程中发生的异常事件对应的类的根父类。
Throwable中的常用方法:
public void printStackTrace():打印异常的详细信息。包含了异常的类型、原因、出现的位置、在开发和调试阶段都得使用printStackTrace。
public String getMessage():获取发生异常的原因。
9 try中声明的变量,出了try结构之后,就不可以进行调用了。
10 对于运行时异常,开发中,通常就不进行显示的处理了。根据异常的提示信息修改代码即可。
对于编译时异常:一定要进行处理。否则编译不通过。(编译时异常虽然处理了,运行时还可能出现,相当于把编译时异常延后到运行时出现)
11 finally中的代码一定会被执行。唯一的例外,使用System.exit(0)来终止当前正在运行的Java虚拟机。
12 什么样的代码一定要声明在finally中:输入流、输出流、数据库连接、Socket连接等资源的显式关闭。因为GC不会自动的回收这些资源(流对象除了变量的指针还可能有其他指针)进而导致内存的泄露。
13 finalize方法是Object提供的实例方法。
14 为啥子类重写的方法的异常类型不能大于父类的:多态。
同理,子类重写方法的返回值类型不得大于父类的被重写的方法的。
15 如何自定义异常类:①继承于现有的异常体系。通常继承于RuntimeException\Exception
②通常提供几个重载的构造器 ③提供static final long servialVersionUID;
如何使用自定义异常类:满足指定条件的情况下手动throw+自定义异常类的对象,如果自定义异常类是非运行时异常则需处理此异常类的对象。
为什么需要自定义异常类:见名知意。不满足我们指定的条件时,我们有必要指明自己特有的异常类。

十四 序列化

实体对象为啥要实现序列化接口:数据可能跨平台(跨服务器端口不同多个jvm)传输即数据在网络当中传输必须要序列化

  • 19
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_168168ww

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值