JavaSE进阶笔记

文章涵盖了Java的核心概念,包括面向对象的特性如继承和多态,异常处理机制,以及集合框架如HashMap和HashSet的使用。还涉及线程的创建和同步,以及多线程中的死锁和线程池。此外,介绍了泛型、IO流、网络编程和XML解析。最后提到了注解和单元测试的重要性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

sday01

面向对象高级

//不要重复造轮子

继承

可以使得子类具有父类的属性和方法,还可以在子类中重新定义,追加属性和方法

格式:public class 子类名 extends父类名{}

父类也被称为基类、超类;子类也被称为派生类

继承的好处:

  • 提高了代码的复用性(多个类相同的成员可以放到同一个类中)
  • 提高了代码的维护性(如果方法的代码需要修改,修改一处即可)

继承的弊端:

  • 继承让类与类之间产生了关系,类的耦合性增强了,当父类发生变化时子类实现也不得不跟着变化,削弱了子类的独立性

应用

is a 才能使用继承,谁是谁的一种

访问特点(就近原则)

1、子类局部范围找,子类成员范围找,父类成员范围找,如果都找不到则报错(不考虑父亲的父亲)

//this代表当前对象的引用,谁调用我,我代表谁

//super代表父类对象的引用

2、子类中所有构造方法都会访问父类中空参构造方法;因为子类会继承父类中的数据,可能还会使用父类的数据,在使用之前需要初始化(构造方法作用:创建对象,初始化数据)

//子类构造方法第一行默认super();

//子类构造方法不能继承父类构造方法

3、成员方法在本类或者子类范围找,父类范围找,都找不到就报错(不考虑父亲的父亲)

方法重写

子类中出现了与父类相同的方法声明(返回值类型,方法名,参数列表

应用场景:子类继承父类方法同时,有自己独特的功能

//方法名+回车–重写快捷键

@override;//注解,检测当前的重写是否正确

//父类方法中不需要写方法体

//私有的方法不能被重写

//子类方法的访问权限不能比父类更低,public -->默认–>private

//类支持单继承,不支持多继承,能多层继承,一个类可以有多个子类

修饰符

分类:权限修饰符、状态修饰符

权限修饰符:public修饰成员方法;private修饰成员变量

public–>protected–>默认–>private

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UIqwKx47-1680336021352)(D:\学习资料\1笔记\image\1.png)]

状态修饰符:

final(最终态),修饰成员方法,成员变量及类,局部变量

格式:

public final class Animal(){}

Animal 变成了最终类,最终类不能被继承

public final void eat(){}

eat变成了一个最终方法,最终方法不可以被重写

private final String name=“张三”;

name变成了一个常量,不能被再次赋值

//final修饰局部变量:

//若变量是基本数据类型,具体数据值不能改变;若为引用数据类型,地址值不能改变,但是地址里边的内容可以发生改变

static(静态)–可修饰成员变量和成员方法

static修饰的变量可以用类名调用可以用对象名调用,建议用类名调用;

特点:被该类的所有对象共享;优先于对象的创建而加载到内存

//修饰的变量在类加载时初始化,留在内存的方法区

//main方法中定义其他方法需要有static;静态成员方法能访问静态成员变量,能访问静态的成员方法–静态只能访问静态

//工具类:构造方法私有(不能被随意访问),成员方法static修饰(用类名调用,不需要创建对象)

day02

多态

一个对象的不同种状态

使用前提要求:

1、要有继承/实现关系

2、要有方法重写

3、父类引用指向子类对象

访问特点:

成员变量

编译看左边(父类),运行看左边(父类)

成员方法

编译看左边(父类),运行看右边(子类)–方法有重写

使用场景:

主要体现在方法的参数上,喂养动物可以是狗、猫等

好处:

提高了程序的扩展性

弊端:

无法使用子类独特功能

转型:

向上转型:父类转为子类,父类引用指向子类对象

格式:Animal a=new Dog();

向下转型:子类转为父类,父类引用转为子类对象

格式:Dog d=(Dog)a;

//instance of 判断左边的对象是否是右边类的具体实例

抽象类

一个没有方法体的方法应该定义为抽象方法,抽象方法必须在抽象类中

格式:public abstract void 点蜡烛();

应用场景:

如果一个方法需要重写,需要定义为抽象方法

快捷键:alt+回车,全部重写

//抽象类不能直接创建对象–抽象类中可能有抽象方法,抽象方法没有方法体,没有必要创建,可以通过多态的方式,用子类实例化

//抽象类中可以有成员变量

//抽象类可以有非抽象方法

//有抽象方法必须是抽象类

//抽象类中可以有构造方法–目的是为了初始化数据

//抽象类的子类不一定要实现父类的抽象方法,子类也可以成为抽象类,但是不推荐

//非抽象类有的抽象类有,非抽象类没有的抽象类也有

接口

就是一种公共的规范标准,只要符合标准,大家都可以用,更多的体现在行为的规范上

//只允许有抽象方法,可以用多态的方式创建对象

格式:

public interface 接口名{}

实现类–InterImpl implements Inter{}

//父类/接口的引用指向子类/实现类对象

注意:

//如果一个抽象类中全部是抽象方法,那么这个抽象类应该被定义为接口

//接口中不允许存在变量,默认为final修饰成为常量,变量默认为static修饰,能被类名及对象调用,默认为public 修饰

//接口中没有构造方法及非抽象方法,默认的都为抽象方法

类与接口关系

类与类之间是继承关系,支持单继承,不支持多继承;

类与接口之间是实现关系,支持单实现,也支持多实现,也可以继承一个类的同时多实现;

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

//一个类如果没有直接的父类,那么它就继承自Object,所有的类都直接或间接的继承Object

接口新增方法–有方法体不需要重写

默认方法:

格式:public default void method(){}

静态方法:

格式:public static void show(){}

私有方法:

private void show3(){}

复习day01-day07

java跨平台原理

‘a’+5=102

++ --在自增自减时运算直接得到结果

if里边是比较表达式+语句体

switch里边是普通表达式+case 值+语句体

for里边是初始化语句、比较表达式/条件判断语句、步进语句/条件控制语句、循环体语句(初始化语句可以写在外边,内部写个分号)

break在循环与switch中

循环使用前提:重复且有规律

数组:存储同种数据类型数据的容器

[]数组标识,()方法标识,{}代码块标识

方法:输出调用、直接调用、赋值调用(.var快捷键)

//有返回值才能使用输出调用

方法重载表示关系

//把当天问题解决,作业写完(看笔记,问同学,找老师),再预习

创建对象,空参构造使用比较多;从右往左写

封装:隐藏实现细节,提供访问的方法,提高安全性,复用性

jar包的制作及使用

制作jar包

1、添加文档注释,artifical–build

2、使用-lib-add as library

制作帮助文档

tools-javadoc

zh_CN

-encoding UTF-8 -charset UTF-8

复习day08-day09-测试

String s1=“abc”;//001

String s2=“ab”;//002

String s3=s2+“c”;//003,有变量,开辟两个空间

substring(2,5);//[2,5),操作后对原数据无影响

String数据类型默认值为null,但是对象为空,打印为空

s.sout–快捷键

科学编程学习法:

获得某个字符串指定索引位置的字符

参数:int index

返回值类型:char

大胆猜测(返回值类型及参数)

API上找

数据量比较大用StringBuilder

equals比较的是同种类型

ArrayList实现了list接口,集合的底层是数组,创建了一个新数组,将旧数组元素放入新数组并扩容为length+length/2,再添加新元素到新数组

day03

内部类

类的内部定义一个类

格式:修饰符 class 内部类名{}

访问特点:

可随意访问外部类成员(包括私有)

外部类访问内部类成员,需要创建内部类对象

主方法中创建内部类对象格式:

外部类名.内部类名 对象名=new 外部类对象().new 内部类对象();

若有内部类私有,创建外部类对象,调用方法

静态成员内部类的创建对象格式:(了解即可)

外部类名.内部类名 对象名=new 外部类名.内部类名();

局部内部类(几乎不用)

方法中定义的类

访问方式:只能在方法中访问

//局部内部类不能私有,只能默认权限

匿名内部类–特殊的局部内部类

使用前提:

存在一个接口/类(抽象类,具体类)

对于一个方法,参数是一个接口,可以使用匿名内部类方式

格式:

new 类名/接口名{重写方法};

//把实现接口、重写方法、创建对象合为一步

//本质是继承了/实现了该类/接口的子类/实现类对象

//只能调用接口中的其中一种方法,若为多种,使用多态(父接口引用指向实现类的对象)

Lambda

1、方法需要一个接口,所以给了一个实现类对象

2、创建了匿名内部类对象,重写方法

3、方法目的–打印一句话

函数式编程思想

强调做什么

//面向对象思想强调通过对象的形式做,谁做及怎么做

使用前提:一个接口,接口中只有一个抽象方法(可以通过@FunctionalInterface验证是否为函数式接口)

//函数式接口:有且仅有一个抽象方法

格式:三要素–形式参数,箭头,代码块

()->{}

//能使用匿名内部类不一定能使用Lambda表达式

省略规则:

1、参数类型可以省略,不能只省略一个

2、参数有且仅有一个,小括号可以省略

3、代码块只有一句,return关键字可省,大括号,分号可以省略

//匿名内部类相对用的更多,可以是接口,可以是抽象类,可以是具体类,编译之后会产生一个单独.class字节码文件

day04

API

math类没有构造方法,static修饰,可以用类名调用

System类status中数字只是JVM识别终止的位置

Object类:父类不满足当前需求,右键重写父类toString方法(或者alt+insert快捷键),运行看右边,获取具体属性

//标准JavaBean还有toString方法

重写equas方法可以用来比较内容属性

Objects-工具类

.isNull()判断是否为空

Decimal类:可以更精确

//除不尽,需要加入精确位数,舍入模式

ROUND_UP 进一法

ROUND_FLOOR 去尾法

ROUND_HALF_UP 四舍五入

包装类

Integer类中不是纯数字会报错,数字解析异常

创建对象:

1、利用有参构造方法

2、利用静态成员方法,.valueOf

自动装箱:基本数据类型转化为包装类类型

自动拆箱:包装类类型转化为基本数据类型

//null不能调用valueOf()方法,使用自动装箱需要先判断是否为null

.parseInt(s)方法可以将字符串纯数字转化为int基本数据类型

.valueOf(i)可以将基本数据类型转化为字符串类型

//字符串切割split会到字符串数组中

数组高级操作

二分查找

mid=(min+max)/2–min=mid+1

冒泡排序----要求手写出来

对进行排序的数组中相邻数据一一比较,大的放后边,一轮一轮比较

//外层循环次数=数组长度-1

ctrl+alt+m–抽取方法

//Arrays.toString(arr)

day05

Arrays类

//二分查找只能是有序的;若元素存在,返回对应索引;若不存在,返回-插入点-1(插入点?指的是如果这个元素存在,应该在哪个位置)

//Arrays.sort(arr,Collections. reverse order())可以从大往小排(数组需要Integer类型)

递归-recursive

方法定义中调用方法自己

//可以将复杂的程序用简单的程序表示出来(时间复杂度低)

//要有出口,不宜过多,否则会溢出

不进入堆内存,在方法区与栈内存

//谁调用方法返回给谁

异常

程序出现了不正常的情况(本身可以处理)

//语法错误不算异常范围内

//声明的异常属于编译时异常,运行时异常不可抛出

异常是JVM打印出来的,当代码出现异常,就会创建一个异常对象,接下来看程序有没有处理异常,如果没有,就会交给本方法的调用者处理,

JVM默认处理了这个异常:

1、将异常打印在控制台

2、停止这个程序,哪里出错,就停止在哪里,后边的代码不执行了

//null不能调用相关的属性或者方法

声明异常的目的:

运行时异常:

告诉本方法的调用者,可能出现该异常,

没有出现,程序正常执行,

出现异常交给方法的调用者(可以不抛出,不声明),即JVM

编译时异常:

必须在方法中声明异常

//谁调用谁处理

声明异常关键字:throws

在方法声明后面,跟的是异常类名

声明的异常可能出现

可以声明多个

抛出异常关键字:throw

在方法体内,跟的是异常对象名

抛出的异常一定出现

只能抛出一个

格式:throw new 异常();

方法内,手动抛出,下面代码无法执行

应用:

1、告诉调用者方法中出现问题

2、在方法中,参数出现问题,抛出,下边代码无法执行

try-catch(重要)–处理异常

try{

可能出现异常的代码;

}catch(异常类名 变量名){

异常的处理代码;

}

快捷键:ctrl+alt+t—try catch

如果try中没有出现问题,try中执行,catch中不执行

如果try中有问题,try中不执行,catch中执行

如果出现的问题没有被捕获,交给JVM处理

如果出现多个异常,最下面的可以用父类Exception

//理论是第一个catch可以是父类Exception,但是不建议,不容易区分是何种异常

e.getMessage();–返回throwable详细的字符串

e.toString();—简短描述

e.printStackTrace();–用的会多一点(红色标记),自己处理的不影响程序执行(表面跟JVM处理一样)

自定义异常类-重点

继承RuntimeException

jdk中没有定义异常,自己根据实际业务定义异常–类名要加Exception

继承父类

添加有参无参构造器

/*
子父类的异常:

  • 如果父类抛出了异常,子类重写父类方法时,可以不抛出异常或者抛出父类相同的异常或者是父类异常的子类
  • 父类方法没有抛出编译异常,子类重写父类该方法时也不可抛出编译异常
    如果子类产生编译异常,只能捕获处理,不能声明抛出

注意:
父类异常是什么样,子类异常就什么样

*/

时间日期类

时间原点:1970年1月1日00:00:00

Date精确到毫秒

Date()返回当前计算机时间

Date(long date)从标准基准时间起指定的毫秒数–(0L)–东八区+8h

getTime()–标准时间到现在的毫秒值

setTime()–设置时间

day06

SimpleDateFormat类

format方法–日期格式化(从Date到String,加个yyyy-MM-dd可以转化为指定的格式)

parse方法–解析文本以生成日期(从String到Date,要解析的日期与指定格式要一致)

集合

数组与集合的区分:

  • 数组:长度(不变)/内容(基本数据类型及引用数据类型)/类型(同种)

  • 集合:长度(可变)/内容(引用数据类型)/类型(可存储多种)

Collection单列集合顶层接口

-List-(ArrayList、LinkedList)

-Set-(HashSet、TreeSet)

map双列集合的根接口

Collection-单列集合顶层接口

用多态形式创建对象

看源码如果是函数式接口,可以用Lambda表达式

clear清空

contains判断是否存在指定的元素

Collection集合的遍历

iterator–迭代器

Iteratoriterator—创建迭代器对象,默认指向0索引处

利用迭代器里边的方法进行遍历

.hasNext()–当前位置是否有元素可以被取出,若为true可以取出,若为false,结束循环

.next()–取出当前位置元素,索引后移一位

//迭代器属于list的一个方法

List

可以存储重复的元素

ArrayList

用迭代器删除元素,只能用迭代器的方法–元素被删除后,内部索引会自动前移;

//迭代器主要用来遍历时删除集合中的元素

集合结构发生变化,需要重新创建迭代器对象

//一个迭代器只能使用一次

//不能轻易的删除

增强for循环----用于遍历,简化for循环的遍历

迭代器及增强for使用前提:实现iterable接口的类

格式:

for(类型名 变量名:arrayList){}

快捷键:arrayList.for+回车

//底层也是迭代器

//修改第三方变量不影响原有内部元素

/*操作索引,用普通for;

删除集合元素,用迭代器

仅仅遍历,用增强for*/

List接口的特点:

1、可以存储重复的元素

2、有索引–可以用于操作索引(Collection中没有操作索引的方法)

3、存取有序–存进去怎样,取出来怎样

数据结构:

栈:栈顶,栈底,先进后出

队列:后端,前端,先进先出,后端进前端出

数组模型:查询快,增删慢

链表模型:通过结点连接成一条链,上一个结点记录下一个结点的地址值----查询慢,增删快

//结点包括两部分:值及地址

//有单向链表和双向链表

ArrayList(jdk1.8)源码解析

扩容步骤:先扩容,再添加元素

创建ArrayList集合对象后,开始长度为0,没有分配;第一次添加元素后长度成为10

LinkedList(增删好)

双向链表

.addFirst()–在该列表开头插入指定的元素

day07

alt+7–快捷键得到类的结构大纲

ctrl+n–搜索某个类

泛型

把运行期间可能出现的问题提前到编译出现

不需要向下强转–避免强制类型转换

set

接口特点:

1、不可以存储重复的元素

2、没有索引–不能用普通的for循环,可以用迭代器和增强for

3、存取无序–可以将元素按照规则进行排序

TreeSet

继承Set接口特点:

要使用TreeSet,一定要有排序规则

comparable接口(要加泛型–与集合泛型相同)----要重写compare To方法

主要判断条件:

int result=this.age-o.age;//this表示当前要存入的对象,o表示已经存进去的对象,o.age-this.age从大到小排序

次要判断条件:

用name的compare To方法判断姓名是否相同,不相同就存入//字母根据ASCII码表比较,this.name与o.name控制顺序,从小到大

int result=o1.age-o2.age(o1为要存入的元素,o2为已经存入的元素,此时为从小到大)

return result;

自然排序原理:

若为负值,当前元素为较小值,存左边;若为0,重复,不存;若为正数,存入的元素时较大值,存右边

//compareIgnoreCase–忽略大小写

Comparator比较器排序

new Comparator+回车

函数式接口可以用Lambda表达式//对于接口,可以用匿名内部类–可简化

String/Integer在java中已经实现了Comparable接口,不需要实现Comparable接口,默认按照字典排序

//自然排序不能满足我们的需求,就需要用比较器

//TreeSet底层是红黑树结构

数据结构–树

二叉树:

一个节点最多只有两个子节点

度:一个节点的子节点数量

树的最高层数为树高

最顶层为–根节点

根节点左边为左子树,右边为右子树

二叉查找树:(二叉排序树、二叉搜索树)

特点:属于二叉树,并且左子节点比自己小,右子节点比自己大

添加节点规则:

小的存左边,大的存右边,一样的不存

平衡二叉树:

特点:二叉树左右两个子树的高度差不超过1;任意节点的左右两个子树都是一棵平衡二叉树

旋转机制触发时机:

左旋:

增加节点时导致树不平衡就会触发旋转机制,将根节点的右侧往左拉,原先的右子节点变成新的父节点,并把多余的左子节点出让,给已经降级的根节点当右子节点

右旋:

与左旋对应

旋转的四种情况:

左左–根节点的左子树的左子树有节点插入,导致二叉树不平衡

如何旋转: 直接对整体进行右旋即可

左右–根节点的左子树的右子树有节点插入,导致二叉树不平衡

如何旋转: 先在左子树对应的节点位置进行左旋,再对整体进行右旋

右右–根节点的右子树的右子树有节点插入,导致二叉树不平衡

如何旋转: 直接对整体进行左旋即可

右左–根节点的右子树的左子树有节点插入,导致二叉树不平衡

如何旋转: 先在右子树对应的节点位置进行右旋,再对整体进行左旋

红黑树

1972年叫平衡二叉B树,1978年改称平衡树

特点:每个节点可以是红或者黑,不是高度平衡的

红黑规则:

1、每个节点或是红或是黑

2、根节点必须是黑色

3、所有叶子节点(空节点Nil)是黑色

4、不能出现两个红色节点相连的情况

5、对每一个节点,从该节点到所有后代叶节点的简单路径上均包含相同数目的黑色节点

添加节点:

默认为红色,效率更高

结论:1、若为根节点直接变黑;

2、若为非根节点,

父节点为黑,不需要操作;

父节点为红,叔叔节点为红(父节点与叔叔节点设为黑色,祖父节点设为红,若为根节点,再次变成黑色);

父节点为红,叔叔节点为黑(父节点设为黑,祖父节点为红,以祖父节点为支点进行旋转)

day08

HashSet

继承set特点:无索引、存取无序(且没有排序规则)、不重复

两Set区分:

HashSet可以存储null值,作为一种元素,只能存一个;存取无序;底层是哈希表(数组+链表)(依靠equals()和hashCode()方法不重复)

TreeSet不能存储null值;自动排序;底层是红黑树结构(必须给定排序规则,不然就报错;元素不重复和排序)

哈希值(哈希码值)

根据对象的地址或者属性计算出来的int类型整数值

Object类有hashCode方法,返回对象的哈希码值

特点:

如果没有重写hashCode方法,根据对象的地址计算的值,

同一个对象调用hashCode方法,得到的哈希码值相同;

如果重写hashCode方法,根据对象的属性计算的值,

属性相同则哈希码值相同

哈希表–JDK1.7与JDK1.8(面试可能用到)

1.7原理分析:

元素的哈希值和数组长度根据某种算法计算出的索引

创建一个数组(table)长度默认为16,加载因子为0.75;达到16*0.75=12,则扩容

首先获取元素的哈希值和数组的长度得到应存入的位置;

判断该位置是否为null,若是null直接存入;若不是,用equals方法比较属性值,若一样,则不存,若不一样,存入数组,老元素挂在新元素下面

//属性不同,哈希码值有可能一样

1.8原理分析:–提高效率

底层是哈希表(数组,链表,红黑树)

节点个数少于等于8个,用数组+链表

节点多余8个,用数组+红黑树(小于6再次转为链表)

首先获取元素的哈希值和数组的长度得到应存入的位置;

判断该位置是否为null,若是null直接存入;若不是,链表根据equals方法比较存入,红黑树结构根据equals方法及红黑树规则排序存入

//HashSet存储的对象是自定义的,需要保证元素唯一,则必须重写equals()与hashCode()方法

Map

特点

1、单列集合一次存一个元素;

双列集合一次存两个元素(K,V)一一对应,称为键值对或者键值对对象,Entry对象;

//k不能重复(若重复不报错,会覆盖旧值;k可以为null),v可重复

2、创建对象:多态方式创建,实现类为HashMap

3、存取无序

方法

.put()增加元素

.remove()根据键删除键值对对象,返回的是value值

.clear()清除键值对对象

.containsKey()判断是否包含该键,返回boolean值

.containsValue()判断是否包含该值,返回boolean值

.isEmpty()判断是否为空,返回boolean值

.size()长度

遍历map集合

1、.keySet()获取到所有的键,返回值是set集合

.get(key)获取对应的值

2、获取功能

map.entrySet()获取所有的键值对对象

Set集合中装的是键值对对象(Entry对象)

Entry里面装的是键和值

entry.getKey()

entry.getValue()

3、map不能使用迭代器遍历,但是可以转换为set集合

4、.forEach(()->{});遍历map集合

HashMap–重点

继承Map的方法

添加规则:通过键计算哈希值找到索引;若为null存入,若不为null,比较equals()和hashCode()方法,相同不存入,不相同存入

//通过equals()和hashCode()方法保证键的唯一----若是自定义对象,需要重写equals()和hashCode()方法

HashMap源码分析(重要)

TreeMap–需要对键值对排序

原理:红黑树

依赖自然排序或者比较器排序,对键排序

如果键存储的是自定义对象,需要实现Comparable接口或者Comparator比较器

//要求排序就用Tree

可变参数

形参的个数可以发生变化;

在调用的时候可以传递0-N个参数;

在使用的过程中,可变参数实际上是一个数组

格式:

public static 返回值 方法名(数据类型 …变量名){}

//(int number,int…arr)–一个形参+一个可变参数

//(int[]…arr)二维数组可变参数

可变参数放在后边(是一个数组)

用于集合的of方法

创建不可变的集合(JDK1.9后里边有)

单列集合:

List.of();不可增加,不可删除,不可改变

用于集合的批量添加

ArrayListlist=new ArrayList<>(List.of(“a”,“b”,“c”));

Set.of();不能存在重复的元素

双列集合:

map.of();

map.ofEntries(map.entry());

实战复习

新闻热搜

要想TreeSet能排序,需要有添加操作(可以先删除再增添)

增强for循环不能进行增删改操作,需要添加flag,boolean flag=false;如果没有匹配上,就结束排序,如果匹配上flag=true,就排序

源码解析:

ArrayList存储特点:有序且可重复

1.底层:数组 Object数组[]

2.存储元素时,没有对元素做判断

3.Object[]数组有一个长度为0,在第一次执行add方法,会构建一个长度为10的数组

4.扩容为原来的1.5倍

5、增删慢

为何不重复,有序?

//删除元素同时,后面元素前移,后来的排在后边

HashSet特点:无序不可重复

1.数组+链表+红黑树

2.底层是HashMap,它的key值就是用来存储HashSet元素的

HashSet中有个HashMap集合,一个空对象

HashMap存储特点:键(无序不可重复)值(没有特点)

1.底层结构

2.扩容大小为原来的二倍

默认初始容量为16

最大容量为2^30

加载因子为0.75

有一个对象数组(entry对象中有key,value,next)//next使用了链表,LinkedList增删快,entry对象通过链表存储,实现了数组于链表的结合(链表作用就是去重)

一个空数组//table是Map底层存储的数组

(无参构造、put方法)

threshold为16*0.75=12;根据key计算得到一个hash值//根据哈希值及数组长度计算出来的,所以无序

//头插法

//hashMap1.7在极端情况下可以存储多少个元素不扩容?–27个(一条链为12+一行中的15)

一行为啥可以存16个?一列为啥是12个?//行是数组,超过16扩容,列中链表是达到16*0.75=12扩容–再想想

//一个索引位置有哈希冲突,其他位置不能有哈希冲突

增强for循环与forEach的区分?

forEach不能使用基本数据类型包括boolean

增强for循环不能用于增删改,需要标记flag为false,如果找到了,没必要继续排序

//公司缺什么,学什么

day09

HashSet、HashMap重写equals()和HashCode()方法保证元素唯一

TreeSet、TreeMap使用自然排序或者比较器保证元素唯一

Stream流

格式:

filter(()->{})

思想:

流水线,获取Stream流,中间方法,终结方法

三类方法:

1、获取Stream流–创建一条流水线(生成Stream流的方式)

单列集合–集合对象.Stream();

双列集合–不能直接获取,需要间接获取

集合对象.keySet().stream();

集合对象.entrySet().stream();

数组–Arrays.Stream(数组名);

同种数据类型的多个数据–Stream.of(可变参数);

2、中间方法

predicate接口;用一个匿名内部类–实现了该接口的实现类对象

接口只有一个抽象方法test,我们可以使用lambda表达式简化

//test方法,返回true,符合条件的才会返回true,代表数据要留下,返回false就不留(删除)

.filter().forEach();

//.filter()可以多次调用,或者在filter()方法中加&&

.startsWith()首字符为…

常见中间操作方法:

.limit(long maxSize);截取前多少个数据

.skip(long n);跳过前n个数据

.concat(Stream a,Stream b);合并两个流

.distinct();去除流中重复的元素,依赖(equals()和hashCode()方法)----根据Object.equals(Object)组成的流

//limit和skip可以结合使用获取中间的几个元素

3、终结方法–最后一步操作

1、forEach(()->{});

Consumer接口中的方法,只有accept(T t);对给定的参数执行此操作

2、.count();返回此流中的元素数,返回值为long

收集操作

1、Stream流中无法直接修改集合、数组等数据源中的数据//要获取新的数据需要重建新的集合对象

.collect(Collectors.toList())负责收集数据,获取流中剩余的数据,但是不负责创建容器(list负责,可重复,底层创建一个List集合对象,并把数据添加到集合当中),也不负责把数据添加到容器中

//.collect(Collectors.toSet());不重复

.collect(Collectors.toMap())用的不多

//流遍历后再次使用需要再次创建流

File

IO流

将数据从从本地文件读取出来并存储到本地文件

文件在哪->路径

File类:

把文件和目录通过File封装成对象

File类就是当前系统中文件或者文件夹的抽象表示
通俗的讲就是使用File对象来操作我们电脑系统中的文件或者文件夹

构造方法:

String path=“c:\itheima”;

1、File file1=new File(path);//path为字符串形式

2、File file=new File(path1,path2);//可以用于路径拼接,前者为父路径名,后者为子路径名

3、File file=new File(file1,path2);//可以用于路径拼接,前者为父路径名,后者为子路径名

//java中一个斜杠\表示转义符;打印引号需要"(两个斜杠相当于一个斜杠)

绝对路径与相对路径

绝对路径:从盘符开始—用的多,安全

相对路径:–测试时会用

1、当前项目下文件(右键file)

2、当前模块下,File file=new File(“模块名\文件名”);

成员方法

创建功能:

.creatNewFile();创建一个空文件,若存在返回false,若不存在,返回true//不管调用者有没有后缀名,创建的是文件(但是不知道是啥文件)

//会有异常,抛出或者try catch

.mkdir();只能创建单级文件夹,不管调用者有没有后缀名,创建的是单级文件夹,返回值为boolean类型//可以不用

.mkdirs();可以创建单级文件夹,也可以创建多级文件夹,不管调用者有没有后缀名(有后缀也是文件夹名),创建的是文件夹,返回值为boolean类型

删除功能:

.delete();//通过代码删除文件或者文件夹要慎重,可以删除具体的文件或者文件夹(文件夹需要是空的),而且不走回收站(不是空的,可以用递归的方法)

.isFile();判断当前抽象路径名是否为文件

.isDirectory();判断当前抽象路径名是否为文件夹(即为目录)

.exists();判断当前抽象路径名对应的文件或者文件夹是否存在//!file.exists()–不存在

.getName();获取当前抽象路径名的文件或者目录

//1、如果调用者是文件,那么获取的时文件名和后缀名

//2、如果调用者是一个文件夹,那么获取的是文件夹的名字

.listFile();返回值是一个File类型的数组//获取的是文件夹中的所有文件夹包括隐藏文件及文件夹

注意事项:

1、调用者是一个文件或者不存在时,返回null

2、调用者是一个空文件夹时,返回数组长度为0的数据

3、调用者是一个有内容的文件夹时,file对象中的文件及文件夹放入数组

4、调用者是一个有权限才能进入的文件夹时,返回null

//目录不存在,才能创建文件夹,需要进行判断,有文件夹后才能创建文件

day10

IO流

目的:将数据写到本地文件,或者将本地文件读出

//I表示读,从硬盘(数据源)到内存;O表示写,从内存到硬盘(目的地)

//流的本质就是数据的传输

分类:

按流向:输入输出

按操作类型:字符流、字节流

//纯文本文件就是能用记事本打开并读得懂(.txt),才能用字符流,否则用字节流(可操作所有类型文件)

字节流

FileOutputStream

1、创建字节输出流的对象–关联到指定路径//直接父类是OutputStream,是所有输出流的超类

//如果关联的文件不存在就会自动创建出来;如果文件已经存在,会先清空原文件所有内容,再写入新添加内容

FileOutputStream fos=new FileOutputStream(new file());//参数可以是字符串,默认为new了一份文件

2、写数据

.write();

//写的是整数,但是实际存入的是整数在码表值中对应的字符

3、释放资源–关流

.close();

//告诉当前这个关联的文件已经使用完毕了,操作流对象完成后需要关闭,不能忘记,否则系统会认为仍在执行

//三个方法都会有异常,需要抛出或者try catch

写数据的三种方式

可以一次一个,一次多个(先创建byte[]数组,再存放全部),一次多个(先创建byte[]数组,再存放全部,从某个索引开始的几个数据)

//文件已经存在,先清空再重新写

两个小问题

需要换行:加.write(“\r\n”.getBytes());或者"\r".getBytes();

追加写入:续写开关,默认为false,先清空;append为true,不清空直接续写

异常处理

抛出交给JVM处理

或者try catch

FileOutputStream fos=null;//需要初始化,不能只声明
try{
    
}catch{
    
}
finally{//不管怎么样,里边的代码一定会执行(给关流提供一个位置)
    fos.close();//可以继续try(判断不是null) catch
}
FileInputStream

创建字节输入流对象-关联指定文件(只能是存在的文件)

读数据

.read();//读的是字节,返回值是码表对应的整数,如果是字符,强转为char

释放资源(关流)

为啥不是97?(转成字符了)而且只有文件中的一个?(只读取一个)

如果文件中有多个字节,判断不是-1,读取并打印

//(char)fis.read()不能读取完全?//read方法只能调用一次

提高拷贝效率的解决方案

一次读多个字节,创建一个字节数组//读取的是字节个数(太大会浪费),不是字节

缓冲流

底层定义了一个字节数组,长度为8192(1024*8)

BufferedInputStream//参数为new FileInputStream(),属于字节流

BufferedOutputStream

仅仅提供缓冲区,而真正的读写数据还得依靠基本的字节流对象进行操作

//不能直接操作文件,需要传递字节流

缓冲流结合数组进行文件拷贝

可以一次读取一个字节数组

字符流

将字符变成二进制存到计算机,叫编码;

将计算机中的二进制数解析显示出来,叫解码

//编码与解码方式一样才不会乱码

编码方式:

ASCII码表没有中文

GBK-有中文(中国推出的)(一个中文两个字节)//大陆买的电脑是GBK编码—windows默认gbk

UTF-8最常见–Unicode–(万国码)–idea或者工作中默认使用,一个中文三个字节

//编码时会有异常,抛出

编码:

.getByte();–默认为UTF-8

.getByte(“gbk”);

解码:

new String(bytes1);–默认为UTF-8

new String(bytes1,“GBK”);

字符流=字节流+码表

1、汉字第一个字节默认为负数,UTF-8中三个作为一个整体转化为中文

2、要拷贝,用字节流或者字节缓冲流

3、文件中的数据读取或者写入用字符流

实战复习

程序员乐趣:解bug

true:不是保留字,不是关键字,是字面常量(还有false),属于布尔常量

LinkedList插入性能比ArrayList高,但是ArrayList尾部插入更快一些

子类继承了父类的所有属性和方法或子类拥有父类的所有属性和方法是对的,只不过父类的私有属性和方法,子类是无法直接访问到的。即只是拥有,但是无法使用

迭代器是遍历集合元素的方式,只遍历不取,遍历与取出元素是不同的;.next();方法返回当前指向元素,然后位置后移一位

String s = new String(“abc”); 创建了2个String Object//字符串常量池(new String是一个),本身有abc就是一个,没有就是两个;String s=“aaa”+“bbb”;创建一个aaabbb对象

day11

字符流

//学的是思路

字符输出流

FileWriter

创建字符输出流对象–关联指定文件路径(如果关联的文件不存在则创建;存在则清空)

//需要保证父级路径存在

写入数据(可以一次写一个字符,一次写一个字符数组,一次写一个字符数组的一部分,写一个字符串,写一个字符串的一部分)

//存入的整数,写入的是对应的字符

释放资源-关闭流

底层:字节输出流+编码表

.flush();//刷新完可以继续在文件中写入数据

//写完之后需要再刷新才能写入(关流后不能再写入),边写边刷新最好,一次刷新太多会出现异常

字符输入流

FileReader

//读取时文件不存在就会报错

读数据的两种方式:

一次读取一个字符//.read()

一次读取多个字符(字符数组元素个数为1024的倍数)//.read(chars)

//new String(chars)–字符数组转化为字符串

字符缓冲流

BufferedReader(Reader in)–方法是字符输入流//new FileReader();

BufferedWriter(Writer out)–方法是字符输出流//new FileWriter();

特有功能:

.newLine();写一行行分隔符//跨平台换行符,在Writer里边

.readLine();读一整行//如果读不到数据返回null,判断不为null,就读,在Reader里边

//int转化为String–+“”;

转换流

InputStreamReader–输入流//字节输入流转换为字符输入流

new FileInputStream(“文件路径名”,“gbk”);//字节流

OutputStreamWriter–输出流//字节输出流转换为字符输出流

//JDK11后新推出了一个构造charset forname"";不再需要转换流

//解决电脑编码与idea编码不一致的问题

对象操作流

特点:把对象以字节的形式写到本地文件,直接打开文件,需要时用对象操作流读到内存中//简化代码

对象操作输入流//对象反序列化流

ObjectInputStream

Student stu=(Student)ois.readObject();

ArrayList list = (ArrayList) ois.readObject();//转成list可以用于遍历

对象操作输出流//对象序列化流,在网络中操作对象或者把对象写入本地文件

ObjectOutputStream(OutputStream)//内部是字节流

//要这个类序列化,需要实现Serializable接口

//性别以后会用0,1,2(int类型)

把对象序列化本地,修改对象后,产生新的序列号,本地的序列号与类中序列号不一致,报错

//解决方式:给对象加一个序列号—(ctrl+n)ArrayList中第一行复制serialVersionUID

对于成员变量不需要序列化,加一个transient

Properties

Map体系集合;有IO相关方法;只存字符串

.put();增

.remove();删

.put();改(根据键相同)

.get();查

遍历:

.keySet();

.entrySet();

特殊方法:(不怎么用)

.setProperty();–.put();//底层调用Hashtable方法put

.getProperty();–.get();

.stringPropertyNames();返回一个不可修改的键集

重要

.load();//将本地文件中的键值对数据读取到集合中

//文件后缀为properties,配合IO流(reader)非常方便,创建properties对象后用prop调用;空格跟分号不要用,会当作内容的一部分

.store();//将集合中的键值对数据保存在本地,配合Writer使用

//不想要注释,用null就可

day12

多线程

理解:同时执行多个任务,但是某个瞬间只做一件,由于切换速度快,认为是同时执行(需要硬件cpu的支持)

实现多线程

并发:同一段时间内,多个指令在cpu上交替执行

并行:同一时刻,多个指令在cpu上同时执行

进程:正在运行的软件(独立性,动态性,并发性),操作系统分配和调度的最小单元

线程:进程中的单个顺序控制流,是一条执行路径,进程中做的事情,cpu调度的最小单元(属于进程的一部分)

//单线程:一个进程只有一条执行路径;多线程:一个进程有多条执行路径(可以有主线程与子线程)

实现方式

1、继承Thread类

步骤:

自定义类继承Thread类;

重写run();//在类中,封装的是启动多线程之后的代码逻辑;如果在主方法中调用相当于调用的是普通方法,属于顺序调用

创建自定义类的对象;

启动线程t1.start();//可以交替调用执行,达到多指令交替执行的目的,底层是由Jvm虚拟机执行的线程的run()方法

//线程执行特点:随机性

//汇编生c,c生万物(c控制底层)

2、实现Runable接口

步骤:

自定义一个类实现Runnable接口;//内部有run方法

重写run();

创建一个自定义类对象,创建一个线程类对象;

将自定义类对象当作参数传入线程类对象;

启动线程

3、实现Callable接口(要加泛型)、FutureTask(加相同的泛型)

步骤:

自定义一个类MyCallable实现Callable接口;

重写call方法;

创建MyCallable及Future实现类FutureTask对象,将MyCallable对象作为构造方法参数传入;

创建Thread对象,将FutureTask对象作为构造方法参数传入,继承Runnable接口;

启动线程;

ft.get()获取结果

//.get();要放在.start()之后,返回String

//创建FutureTask对象目的:获取多线程执行的结果;拿到一个参数传入Thread

三者的区分使用

不需要返回结果:实现Runnable接口

需要返回结果:实现Callable接口

不需要扩展继承其他的类:继承Thread类

获取及设置线程名称

getName();----格式:Thread-编号//类中调用

.setName();----不能直接作为类对象的参数,Thread中有一个有参构造方法可以直接传入,类中加入有参无参构造方法//此方法在主方法中调用

//.currentThread();//Thread的一个静态方法,返回对当前正在执行的线程对象的引用,可以获取的是当前线程的名称

实现runnable接口,.currentThread().getName()

//继承线程可以直接用getName()方法

线程休眠

.sleep();线程中的一个方法,休眠毫秒数(long类型)

线程调度

分时调度模型:轮流获得cpu的使用权,平均分配时间片

抢占式调度模型:优先级高的先使用(java采用)

优先级:

.getPriority();获取优先级(1-10),默认为5

.setPriority();设置优先级

守护/后台线程

.setDaemon();内置boolean类型true,则设置为守护线程

//普通线程执行完之后,守护线程没有执行下去的必要了,但是仍在cpu中有执行权,不会立即停止

线程安全问题

卖电影票中会出现负数票(每个线程都会做一次判断)或者重复票

//多个线程操作共享的数据导致

解决方案:

1、把操作共享的数据锁起来,一次只能有一个线程操作,使用同步代码块实现(重要用的多)

synchronized(锁对象){操作共享数据的代码}

//锁对象要唯一,但是可以是任意对象:private Object obj=new Object();传递obj锁对象

创建多个类对象时,需要静态修饰

锁对象为this,表示当前锁对象//不能解决线程安全问题

成员变量静态修饰,作为资源共享数据,优先于对象加载到方法区

锁对象静态修饰

2、同步方法锁对象是this,代表当前对象的引用,谁调用指的谁

修饰符 synchronized 返回值 synchronizedMethod(参数){方法体}

静态同步方法锁对象:类名.class

同步方法与同步代码块可以结合起来使用

3、Lock锁-jdk5推出

private ReentranLock lock=new ReentranLock();

.lock();//上锁

.unlock();//释放锁

死锁

两个或者多个线程互相持有对方需要的资源,都需要等待

不写锁的嵌套就可以避免死锁问题

//存在一个匿名内部类的使用

生产者与消费者

.wait();导致当前线程等待和唤醒

.notify();唤醒单个等待线程

.notifyAll();唤醒所有等待线程

day13

多线程高级

线程状态

线程对象在不同时期有不同的状态

六种状态:

新建状态(NEW)

就绪状态(RUNNABLE)

阻塞状态(BLOCKED)-无法获取锁-获得到锁

无限等待状态(WAITTING)-notify

计时等待(TIMEDWAITTING)-sleep

结束状态(TERMINATED)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MDOcdHJJ-1680336021353)(D:\学习资料\1笔记\image\Snipaste_2022-05-25_09-51-29.png)]

线程池

池化思想:不需要创建线程对象;减少资源消耗

一个池子存放多个线程

代码实现:

1、创建默认线程池对象(Executors.newCachedThreadPool()),返回的是池子控制对象(左边是控制)

//最多可装int类型的最大值个

//用Executors调用,帮助创建线程池对象

//ExecutorService可以帮助我们控制线程池

//Executors.newFixedThreadPool(10),可以指定相应最多数目的线程,不是初始值

//pool调用.getPoolSize()获取池子中线程数量,向下强转问题

2、.submit(()->{});多个池子控制对象调用,相当于多个线程//有空闲线程直接用,没有就新建一个新的线程(用Thread调用.sleep()方法更能体现出来)–提交任务

3、.shutdown();//关闭线程池后不能再找到空闲线程

//底层是newThreadPoolExecutor()

自定义线程池对象(用的还是比较多)

newThreadPoolExecutor()

参数:

1、核心线程数量-正式员工数量-不能小于0

2、最大线程数-最多能装的员工数量-不能小于等于0,最大数量>=核心线程数量

3、空闲的线程存活时间(值)-如60s-不能小于0

4、空闲的线程存活时间(单位)-TimeUtil.SECONDS-时间单位

5、任务队列-允许最多的排队客户–new ArrayBlockingQueue(10) -不能为null

6、创建线程工厂-从哪里找人–Executors.defaultThreadFactory()-不能为null

7、任务的拒绝策略-拒绝服务–new ThreadPoolExecutor.AbortPolicy();//把多余的任务丢弃并抛出异常(最常用)-不能为null

//什么时候拒绝任务?提交的任务>线程的最大数量+队列数目

//还有三种拒绝策略:(了解)

.DiscardPolicy();//丢弃任务,不抛出异常–不推荐

.DiscardOldestPolicy();//抛弃队列中等待最久的任务,然后把当前任务加入队列中

.CallerRunsPolicy();//自己忙不过来交给别人执行(调用任务的run()方法绕过线程池直接执行)

//要用循环需要重新定义变量,lambda默认为常量

网络编程

在网络通信协议下,不同计算机上运行的程序,可以进行数据传输

三要素

IP地址

能联网的设备比如计算机指定标识(ipv4(32bit,4字节为1组,分配42亿IP),ipv6(128bit,16字节为1组))(相当于身份证)

//cmd ping IP地址–检查网络是否连通;ipconfig–查看本机IP地址;特殊IP地址–127.0.0.1可以代表本机地址,是回送地址,用来测试使用

InetAddress

.getByName();确定主机名称IP地址,参数为主机名或IP地址

.getHostName();返回主机名

.getHostAddress();返回主机IP

//返回值调用成员方法

端口

设备中应用程序的唯一标识

用两个字节表示的整数,取值范围0-65535

0-1023被知名的网络应用使用,我们用1024以上的一个即可

//一个端口号只能使用一次

协议

在数据传输的过程中应该遵守的规则

UDP协议(面向无连接的通信协议)

//速度快,限制大小(64k),不安全,保证不了数据的绝对可靠(对安全性要求不高)

TCP协议(面向连接的通信协议)

//速度慢,不限制大小,安全,数据不会丢失、不会重复

UDP通信程序

发送数据:

1、找码头(创建套接字对象)

创建DatagramSocket对象

2、打包礼物

创建DatagramPacket对象

参数(字节数组,长度,发送到的address,端口号port)

3、由码头发送包裹

ds.send(DatagramPacket dp);

4、付钱走羊(码头关闭)

.close();

接收数据:

1、找码头

DatagramSocket

参数(port)

2、找新箱子

DatagramPacket

参数(字节数组,长度)//byte

3、码头接收礼物并放入新箱子

ds.receive(dp);

4、村长从新的箱子获取礼物

dp.getData()

new String(data,0,dp.getLength())

//也可以不用data,用byte数组

5、拿完走羊

ds.close();

//先运行接收,再运行发送;

//如果接收端再启动之后,没有接收到数据,那么会在接收位置死等(阻塞)

//接收数据时调用getLength();

//idea右上角config可以设置允许多个发送端

//测试阶段用127.0.0.0.1最好

三种通信方式:

单播:两个主机之间的端对端通信

组播:用于对一组特定的主机进行通信

广播:一个主机对所有局域网的通信

组播:

接收端:MulticastSocket

//绑定一个组播地址.joinGroup();–224.0.0.0-239.255.255.255

广播:(与单播相同)

广播地址:255.255.255.255

题目:ArrayList线程安全吗?原因是啥?

不安全,后边的线程会覆盖前边的线程;用Collections类调用synchronizedList将ArrayList集合封装后可以变安全,方法内部有个同步代码块。另外用CopyWriterArrayList底层用了lock锁,使得集合变得安全

day14

TCP通信程序

可靠的网络协议,通信的两端各建立一个socket对象

发送端(客户端)

1、创建一个socket对象

参数:地址,端口号

//套接字:地址:端口号,是一个通信端点

2、获取一个IO流开始写数据

.getOutputStream(调用字节输出流方法)

3、释放资源

//关码头

//只有发送端运行会报异常

接收端(服务器端)

1、创建ServerSocket对象

参数:端口号

2、等待客户端连接,监听套接字

.accept();

//阻塞(作用就是等待客户端连接),返回套接字

3、获得输入流对象,读取数据

accept.getInputStream();

//用socket调用也可以

//read方法也会阻塞,等待read完全

4、释放资源

socket.close();//关流时多了一个往服务器写结束标记的动作,没有就报错,不能结束

ss.close();//四次挥手协议保证终止连接

//通过三次握手协议保证跟服务器之间的连接

三次握手

客户端向服务器发出连接请求,等待服务器确认

服务器返回响应,告诉客户端已收到连接请求

客户端再次发送确认消息,建立连接

四次挥手

客户端向服务器发出取消请求

服务器返回一个响应表示收到取消请求

//服务器处理最后的数据

服务器向客户端发出确认取消信息

客户端再次发送确认消息连接取消

//如果用.close()关闭流,整个Socket无法使用;用.shutdownOutput();仅仅关闭流,写一个结束标记,告诉服务器传输完毕,对socket无影响(发送完给一个结束标记)

//用字符缓冲输入流与转换流结合可以写入中文

new BufferWriter(new OutputStreamWriter(os));

代码练习-输出输入

文件写到服务器:代码练习(重要)

读取本地的流,通过网络流写入到服务器;服务器读取网络流,写入本地流,关流

服务端优化

1、while(true){}//循环使用,可以用来处理多个客户端请求;防止服务端处理一次客户端请求后关闭

2、UUID类产生一个随机且唯一的UUID对象//生成一个随机文件名,不是真正的唯一;防止第二次生成的文件将前一次的文件覆盖

UUID.randomUUID();

uuid.toString()+“.jpg”

3、处理多个客户端请求用多线程

创建一个线程类,run方法不能抛异常,需要用try catch处理

关流在finally中,判断流不为空,try catch

创建对象通过构造传递获得的流数据

新建线程并启动

4、用自定义线程池减少消耗

new ThreadPoolExecutor()

日志

用来记录程序在运行时的点点滴滴,并永久存储

日志与输出语句

输出语句日志技术
取消日志需要修改代码,灵活性比较差不需要修改代码,灵活性比较好
输出位置只能是控制台可以将日志信息写入到文件或者数据库中
多线程和业务代码处于一个线程中多线程方式记录日志,不影响业务代码的性能

日志技术特点:

控制日志信息输送的目的地时控制台、文件等位置

控制每一条日志的输出格式

定义级别,控制日志的生成过程

通过一个配置文件灵活配置,不需要修改应用的代码

Logback-基于sl4j

(还有Log4j不怎么用)

log back.qos.ch/index.html可以下载jar包

有三个模块:logback-core(提供基础代码,必须有);logback-classic(完整实现了slf4j模块);logback-access(提供HTTP访问日志功能)

步骤:

导入Logback的相关jar包(lib)

编写配置文件

获取日志对象

public static final Logger LOGGER=LoggerFactory.getLogger(LogDemo.class)

按照级别打日志(调用.infor()方法)

6种级别:

TRACE<DEBUG(大部分)<INFOR(感兴趣)<WARN(提示)<ERROR(try catch里边)<FATAL(重大错误)

//默认debug

//作用时只输出该等级相同及以上的日志

//ALL和OFF分别是打开全部日志信息,及关闭全部日志信息

控制台日志格式:

%d表示日期,%thread表示线程名,%-5level表示级别从左显示5个字符宽度,%msg为日志消息,%n是换行符

文件:

日志格式、编码表utf-8、日志输出路径

//root可修改级别

//控制台可修改日志格式

//文件可修改日志格式

枚举

enum

格式:

将变量的值一一列出来

public enum Season{枚举一、枚举二、枚举三}

//都要大写,“枚举成员名称必须全部大写,单词之间必须用下划线分隔。 枚举实际上是一个特殊的常量类,缺省情况下,构造方法强制为私有。”

特点:

每一个枚举就是一个枚举对象

类名.枚举项名访问相应枚举项

可以在类中定义成员变量

枚举项放在类下面的第一行

空参有参构造默认private修饰

枚举项中(成员变量参数){}

枚举中有抽象方法,枚举项中必须重写

//public int age;同一个类为啥要public?需要其他类能够访问

方法

.name();获取枚举项的名称

.ordinal();返回枚举项在枚举类中的索引值

.compareTo(E o);比较两个枚举项,返回的是索引值的差值

.toString();返回枚举常量的名称//打印枚举项名字而不是地址

Enum.valueOf(Season.class,name);获取指定枚举类中的指定名称的枚举值

.values();获得所有的枚举项,返回一个数组

//多说多问多敲多练

day15

类加载器

将字节码文件(存储的物理文件,在硬盘中)加载到内存中(送到虚拟机)

//物理删除是从数据库移除,逻辑删除是做标记不再被查询到(我们会用逻辑删除)—数据库每隔10min备份一次

类加载时机

用到就加载,不用不加载

//创建类实例,调用类的类方法,访问类或者接口的类变量或者为该类变量赋值,初始化某个类的子类,使用java.exe命令来运行某个主类,反射方式创建类或接口对应的java.lang.Class对象

类加载过程

1、加载

通过包名+类名,获取类,用流传输;加载到内存;创建class对象

2、链接

验证-是否有安全隐患,是否符合虚拟机规范,是否危害JVM;

准备-对静态成员分配内存,设置默认初始化值;

解析-符号引用替换为直接引用,用到了其他类,需要找到相应的类(如name找到String,age找到int)

3、初始化

为静态变量赋值

//java不能操作内存,需要调用c中的接口操作内存

//重点是企业用到的及面试用到的

类加载器分类

启动类加载器Bootstrap class loader(是Platform父类加载器)–虚拟机内置-默认null

平台类加载器Platform…(是System父类加载器)-负责JDK模块

系统类加载器System…(是User父类加载器)–用的比较多-负责加载用户类路径指定类库

自定义类加载器User…–用的不多

双亲委派模型(重点)

加载器收到任务后不是先自己处理,获取父类后依次向上委托,若能完成成功返回,若无法完成加载任务,向下委托

ClassLoader.getSystemClassLoader();//获取系统类加载器

.getParent();//获取父类加载器

方法

用系统类加载器调用.getResourceAsStream(name);

利用加载器去加载到指定文件,参数为文件路径(放在src下,默认在这里加载),返回值为字节流

创建Properties集合对象,加载这个流

反射

正射:直接创建对象的方式(字节码文件加载到虚拟机)

反射:把虚拟机中的字节码文件剥离出来

用反射调用类中成员变量和成员方法时,可以无视修饰符;

运行状态中,先获取配置文件,对于任意一个类或者任意一个对象,动态获取信息或者动态调用对象任意属性和方法的功能,也称为java语言的反射机制

配置文件:创建对象全类名,调用方法名

特点:不是死的,比较灵活。读到什么创建什么

获取class对象(变量名clazz)三种方式

//为了用反射创建对象,调用里面的成员

反射中不用new创建,用Class类创建

源代码阶段(建立字节码文件):静态方法:Class.forName(“包名+类名”);//copy Reference可以获取参数,返回的泛型可以删除

Class对象阶段(进入内存class对象):class属性:类名.class;

Runtime运行阶段(创建对象):利用对象的getClass方法获取,对象名.getClass();

//三种方式获取的class对象地址相同

Class类中获取constructor四种方法

获取Class对象

调用.getConstructors();方法,返回所有公共构造方法对象的数组

调用.getDeclaredConstructors();方法,返回所有构造方法对象的数组

调用.getConstructor();方法,返回单个公共构造方法对象

//可以是空参,可以加参数(.class),但是需要与形参保持一致

调用.getDeclaredConstructor();方法,返回单个构造方法对象

//成员变量是Filed对象,构造方法是Constructor对象,成员方法是Method对象

用反射创建对象

获取class对象

获取构造方法对象(可以获取私有构造方法)

利用构造器调用.newInstance();方法,创建Student类对象(强转)

//若为空参,利用构造器调用.newInstance();方法,强转;可以用clazz直接调用.newInstance();方法,再强转

//若获取的是私有构造方法,用构造方法对象调用.setAccessible();设为true,取消访问检查(暴力反射,开了一个小通道)–针对Declared方法

反射获取成员变量并使用

1、获得class对象

2、获得Field对象–获取成员变量对象

.getFields();返回所有公共成员变量对象的数组(可以遍历)

.getDeclaredFields();返回所有成员变量对象的数组

.getField();返回单个公共成员变量对象

//获取的成员变量必须真实存在,并且public修饰

.getDeclaredField();返回单个成员变量对象

3、赋值或者获取值

先创建Student类对象

用field对象调用set方法field.set(obj,value);给成员变量赋值为value

用field.get(obj);返回由该Field表示的字段在指定对象上的值

用field.setAccessible();设为true,取消访问检查–针对Declared方法,放在找成员变量的位置

反射获取成员方法并使用

1、获得class对象

2、获得Method对象–获取成员变量对象

.getMethods();返回所有公共成员方法对象的数组(可以遍历)-包括继承的

.getDeclaredMethods();返回所有成员方法对象的数组-不包括继承的

.getMethod();返回单个公共成员方法对象,参数为方法名(方法名必须存在),没有其他参数留空,有参数为.class

.getDeclaredMethod();返回单个成员方法对象(不需要取消访问检查)

3、运行方法

创建Student对象

用method调用.invoke()方法(参数一:方法调用者,参数二:传递的参数-有则写没则不写;返回值有则写没则不写–根据成员方法确定)

//invoke-调用 node-节点、端点

day16

XML

xml(用的更多)与properties配置文件都会在项目时出现

xml提高代码阅读性及维护性

W3C在线网址:(学习xml)
https://www.w3school.com.cn

是一种可扩展的标记语言(通过标签/元素描述数据的一门语言,标签名可以自定义),用于存储数据及传输数据、作为软件的配置文件

标签

格式:由一对尖括号和合法标识符组成

规则:

必须成对出现,尾部标识符前边加一个斜杠;

特殊的标签可以不成对,但是必须有结束标记(左斜杠);

标签中属性和标签名空格隔开,属性值必须用引号引起来;

嵌套时需要完全嵌套

XML语法规则:

后缀名:xml;

文档声明放在第一行第一列顶头写(?属性:version–必须存在;encoding-UTF-8-不是必须存在;standalone(yes or no)–不是必须存在);

//下边为注释内容,加个!

必须存在一个且仅有一个根标签(然后可以加入子标签嵌套)

//加CDATA内容,可以用大于号小于号

//可以用浏览器打开

//快捷键:<?+回车;加>自动生成标签的另一半

解析

DOM解析思想

Document对象:整个xml文档,把文档的各个组成部分看做成对应的对象

Node对象:

Element对象:所有标签

Attribute对象:所有属性

Text对象:所有文本内容

解析工具

DOM4J(全程Document for Java)–用的比较多

JAXP,JDOM,pull

代码实现

1、获取一个解析器对象

//new SAXReader();

2、利用解析器把xml文件加载到内存中,并返回一个文档对象

用解析器对象调用.read();参数为文件对象

3、通过文档对象获取到根标签

.getRootElement();

4、通过根标签获取子标签

.elements();参数可以为空获取所有子标签,也可以有参数为指定的子标签,返回ArrayList集合(泛型为Element),可以遍历集合

5、获取属性及属性值

用标签调用.attribute();

用属性调用.getValue();

6、获取子标签及标签体

.element();参数为子标签的子标签

.getText()

//运行后会有正常的警告信息

注解

对程序进行标注和解释,可以作用在方法上或者类上,给编译器看的

优势:让出错的问题前移,运行阶段前移到编译阶段,提高代码效率

@Override//重写父类方法

@Deprecate//表示方法过时

@SuppressWarnings(“all”)//压制一切无关紧要的警告,不能压制编译错误及编译警告

//注释是给程序员看的

自定义注解

格式:

public @interface 注解名称{public 属性类型 属性名 ()default 默认值}

属性名:基本数据类型,String,Class,注解,枚举,以上类型的一维数组

//默认值可省略,Class默认值是.class字节码文件;注解中没有指定默认值,需要手动给出注解属性的设置值,自动匹配类型,顺序可变

//value这个属性比较特殊,如果没有指定默认的值而且注解只有一个没有指定默认值,那么在使用的时候,属性名可以不加

用成员方法调用.isannotationPresent()方法,参数为注解.class对象,返回布尔值,true为该注解存在,false则该注解不存在

//注解中需要加上元注解,Retention并加上参数

元注解(了解)

描述注解的注解

@Target//指定注解使用的位置(ElementType.成员变量FIELD,类TYPE,方法METHOD)

@Retention//指定该注解的存活时间,.class文件的使用时间,否则停留在源码阶段

@Inherited//指定该注解可以继承

@Documented//指定该注解会在API文档(没啥用)

单元测试(重要)

Junit

开放源代码的测试工具;

提供注解识别测试方法;

提高代码效率及质量,代码更优雅;

进度条为绿色,说明运行良好;红色,说明运行中出现问题

使用流程:

导入jar包到工程(junit-4.9)

使用Junit中的@Test注解测试方法(删除主方法)

//只能操作无参无返回值的非静态方法

其他注解

@before在测试代码之前执行,用于初始化操作

@after在测试代码之后执行,用于释放资源

//HashMap线程不安全,数组长度不是2的n次幂,容易发生哈希冲突;加载因子0.75

e

用field.get(obj);返回由该Field表示的字段在指定对象上的值

用field.setAccessible();设为true,取消访问检查–针对Declared方法,放在找成员变量的位置

反射获取成员方法并使用

1、获得class对象

2、获得Method对象–获取成员变量对象

.getMethods();返回所有公共成员方法对象的数组(可以遍历)-包括继承的

.getDeclaredMethods();返回所有成员方法对象的数组-不包括继承的

.getMethod();返回单个公共成员方法对象,参数为方法名(方法名必须存在),没有其他参数留空,有参数为.class

.getDeclaredMethod();返回单个成员方法对象(不需要取消访问检查)

3、运行方法

创建Student对象

用method调用.invoke()方法(参数一:方法调用者,参数二:传递的参数-有则写没则不写;返回值有则写没则不写–根据成员方法确定)

//invoke-调用 node-节点、端点

day16

XML

xml(用的更多)与properties配置文件都会在项目时出现

xml提高代码阅读性及维护性

W3C在线网址:(学习xml)
https://www.w3school.com.cn

是一种可扩展的标记语言(通过标签/元素描述数据的一门语言,标签名可以自定义),用于存储数据及传输数据、作为软件的配置文件

标签

格式:由一对尖括号和合法标识符组成

规则:

必须成对出现,尾部标识符前边加一个斜杠;

特殊的标签可以不成对,但是必须有结束标记(左斜杠);

标签中属性和标签名空格隔开,属性值必须用引号引起来;

嵌套时需要完全嵌套

XML语法规则:

后缀名:xml;

文档声明放在第一行第一列顶头写(?属性:version–必须存在;encoding-UTF-8-不是必须存在;standalone(yes or no)–不是必须存在);

//下边为注释内容,加个!

必须存在一个且仅有一个根标签(然后可以加入子标签嵌套)

//加CDATA内容,可以用大于号小于号

//可以用浏览器打开

//快捷键:<?+回车;加>自动生成标签的另一半

解析

DOM解析思想

Document对象:整个xml文档,把文档的各个组成部分看做成对应的对象

Node对象:

Element对象:所有标签

Attribute对象:所有属性

Text对象:所有文本内容

解析工具

DOM4J(全程Document for Java)–用的比较多

JAXP,JDOM,pull

代码实现

1、获取一个解析器对象

//new SAXReader();

2、利用解析器把xml文件加载到内存中,并返回一个文档对象

用解析器对象调用.read();参数为文件对象

3、通过文档对象获取到根标签

.getRootElement();

4、通过根标签获取子标签

.elements();参数可以为空获取所有子标签,也可以有参数为指定的子标签,返回ArrayList集合(泛型为Element),可以遍历集合

5、获取属性及属性值

用标签调用.attribute();

用属性调用.getValue();

6、获取子标签及标签体

.element();参数为子标签的子标签

.getText()

//运行后会有正常的警告信息

注解

对程序进行标注和解释,可以作用在方法上或者类上,给编译器看的

优势:让出错的问题前移,运行阶段前移到编译阶段,提高代码效率

@Override//重写父类方法

@Deprecate//表示方法过时

@SuppressWarnings(“all”)//压制一切无关紧要的警告,不能压制编译错误及编译警告

//注释是给程序员看的

自定义注解

格式:

public @interface 注解名称{public 属性类型 属性名 ()default 默认值}

属性名:基本数据类型,String,Class,注解,枚举,以上类型的一维数组

//默认值可省略,Class默认值是.class字节码文件;注解中没有指定默认值,需要手动给出注解属性的设置值,自动匹配类型,顺序可变

//value这个属性比较特殊,如果没有指定默认的值而且注解只有一个没有指定默认值,那么在使用的时候,属性名可以不加

用成员方法调用.isannotationPresent()方法,参数为注解.class对象,返回布尔值,true为该注解存在,false则该注解不存在

//注解中需要加上元注解,Retention并加上参数

元注解(了解)

描述注解的注解

@Target//指定注解使用的位置(ElementType.成员变量FIELD,类TYPE,方法METHOD)

@Retention//指定该注解的存活时间,.class文件的使用时间,否则停留在源码阶段

@Inherited//指定该注解可以继承

@Documented//指定该注解会在API文档(没啥用)

单元测试(重要)

Junit

开放源代码的测试工具;

提供注解识别测试方法;

提高代码效率及质量,代码更优雅;

进度条为绿色,说明运行良好;红色,说明运行中出现问题

使用流程:

导入jar包到工程(junit-4.9)

使用Junit中的@Test注解测试方法(删除主方法)

//只能操作无参无返回值的非静态方法

其他注解

@before在测试代码之前执行,用于初始化操作

@after在测试代码之后执行,用于释放资源

//HashMap线程不安全,数组长度不是2的n次幂,容易发生哈希冲突;加载因子0.75

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

tqs_12345

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

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

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

打赏作者

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

抵扣说明:

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

余额充值