Java SE 学习笔记
Java SE 学习笔记
java介绍
跨平台原理jvm虚拟机
jre和jdk
运行一个java程序jre就可以
开发工具jdk 包含jre和开发工具 jre里面包含jvm以及运行类库
jdk下载官方网站www.oracle.com
简单dos命令 需要掌握
配置变量
HelloWorld案例
编译 javac 文件名.java
运行 java 类名
注释
//单行
/* */多行
文档注释(暂时不用)/** */
关键字
高亮显示
常量
字符串常量 “你好”
整数常量 666
小数常量 12.14
字符常量 ‘A’
布尔常量 true false
空常量 null 这个不能直接输出
数据类型
变量
int a = 10;
变量的使用 取值 a 修改 a = 20
变量使用的注意事项
变量要初始化 未赋值不能使用 名字不能重复
数值默认为int类型
浮点数默认为double类型
定义long类型 防止整数太大问题在值后面加L
定义float类型 防止不兼容的类型 在值后面加上F 转换为float
标识符
标识符:就是给类,方法,变量等起名字的符号
定义规则
- 由数字/字母/下划线_和美元符号$组成
- 不能以数字开头
- 不能是关键字
- 区分大小写
常见的命名约定
小驼峰命名法:方法、变量
约定1:标识符是一个单词的时候,首字母小写
范例1:name
约定2:标识符多个字母组成,第一个单词首字母小写,其余单词首字母大写
范例2:firstName
对应的大驼峰: 类
单词首字母都大写
类型转换
自动类型转换
把一个范围小的数值或者变量赋值给另外一个表示数据范围大的变量
示例:double d = 10;
强制类型转换
把一个表示数据范围大的数值或者变量赋值给另一个表示范围小的变量
- 格式:目标数据类型 变量名 = (目标数据类型)值或者变量;
- 示例:int k =(int)88.88;
强制类型转换不建议 会导致数值的损失
算术运算符
运算符和表达式
- 运算符:进行操作的符号
- 表达式:用运算符把常量或变量连接起来符合java语法的式子就可以称为表达式。不同运算符连接的表达式体现的是不同类型的表达式
算术运算符
+ 加号 - 减号 *乘号 /除号 %取余
除法得到的是商,取余得到是余数
整数相除只能是整数,要想得到小数,必须由浮点数参与。
字符的“+”操作
字符与int做+操作
字符为ASCII值操作 a为97 A为65 0为48
int类型和char类型做+操作 结果是int类型的
如果强制转换为char类型则为对应ASCII数值的字符
字符串的“+”操作
“+”出现在字符串时候,这个是字符串连接符
两个字符串相加为相连
字符串和int也可以相连
第一条结果 黑马666
第二条结果 100年黑马
像是从左到右执行
总结如图
赋值运算符
= 为赋值符
赋值操作从右到左
拓展赋值运算符 +=
示例 i=10 i += 20 结果为30 i+=20 结果与 i = i+20 相同
但是 注意+= 蕴含了强制类型转换
否则 short s = s +20 会出现int转short问题
因为s + 20 会变为int类型 解决办法为强制类型转换 这种情况能用 += 则用+= 避免这种类型情况
其余拓展运算符
注意事项:
拓展的赋值运算符隐含了强制类型转换
自增自减运算符
- 符号++ 自增
- 符号 – 自减
本身增减 与C语言自增自减符号基本相同
注意事项:
关系运算符
关系运算符结果都为布尔型boolean 要么是true要么是false
与C语言基本一致
注意别把==写成=
逻辑运算符
- 逻辑与 & 有false则false
- 逻辑或 | 有true则true
- 逻辑异或 ^ 相同为false,不同为true
- 逻辑非 ! true边false,false变true 结果相反
理论与C相同
短路逻辑运算符
- 短路与 &&
- 短路或 ||
区别 短路与 && 左边为false 则结果为false不执行右边
同理 短路或 || 左边为true 则结果为true不执行右边
最常用的逻辑运算符:&&,||,!
三元运算符
格式:关系表达式?表达式1:表达式2;
数据输入
流程控制
顺序结构
顺序结构按照代码先后顺序执行。 从上往下 一句一句执行
流程图:
分支结构
if语句
流程图:
if ··· else 语句
流程图:
if ··· else if 语句
流程图:
数据测试: 正确数据,边界数据,错误数据
switch语句
break退出
流程图:
无break则执行满足条件下面的所有语句 被称为:case穿透
案例:
注意事项:在switch语句中,如果case控制的语句体不写break,将出现穿透
现象,在不判断下一个case值的情况下,向下运行,直到遇到break或者整体switch语句结束。
循环结构
for语句
流程图:
思考
![image-20230629162123106](C:\Users\李嘉欣\AppData\Roaming\Typora\typora-user-images\image-20230629162123106.png)
示例:
while循环
流程图:
do ··· while循环
流程图:
三种循环区别
do ··· while 至少先执行一次
for 和 while区别
图 for循环 i 的值是属于for循环的 在for外无法使用 而while定义的 j 没有在while内定义所以可以使用
死循环
for( ; ; ) { }
while(true) { }
do{} ··· while(true);
总结:
跳转控制语句
- continue 用在循环中,基于条件控制,跳过某次循环体内容的执行,继续下一次执行
- break 用在循环中,基于条件控制,终止循环体内容的执行,也就是说结束当前的整个循环
循环嵌套
Random
作用:用于产生一个随机数
IDEA
安装配置与创建项目
IDEA项目结构
项目->模块->包->java类 编写代码运行
- 快速生成语句
快速生成main()方法:psvm,回车
快速生成输出语句:sout,回车
- 内容辅助键
Ctrl+Alt+space(内容提示,代码补全等)
注释
单行:选中代码,Ctrl+/,再来一次,就是取消
多行:选中代码,Ctrl+shift+/,再来一次,就是取消
格式化
Ctrl+Alt+L 变为格式化的代码更美观,建议直接按规范来写
模块新建与导入操作
数组
数组array是一种用于存储多个相同类型的存储模型
定义格式:
数组初始化与动态初始化
java数组必须先初始化,才能使用
- 动态初始化
初始化时只指定数组长度,由系统为数组分配初始值
数组的元素访问
arr[0] 访问数组中元素
输出数组名arr 的话结果为arr的内存地址
内存分配
数组在初始化时,会为存储空间添加默认值
多个数组指向相同
指向相同时,任何一个修改堆内存的数据,堆内存值改变,再访问就是改变后的值。
静态初始化
初始化指定每一个数组元素的初始值,由系统决定数组长度。
数组操作的两个小问题
索引越界 访问了数组中不存在的索引对应的元素,造成索引越界
越界 定义3个 内存索引为0 1 2 但是访问3则为索引越界
空指针异常:访问的数组已经不再指向堆内存的数据,造成空指针异常
数组常见操作
遍历
为了方便 java提供了获取数组元素个数
格式为:数组名.length 范例:arr.length
遍历通用格式:
获取最值
方法
概述
方法(method)是将具有独立功能的代码块组织成一个整体,使其具有特殊功能的代码集
- 方法先创建才能使用,该过程称为方法定义
- 方法创建后并不是直接运行的,需要手动使用,该过程称为方法调用
方法定义和调用
格式:
方法调用:
方法先定义再调用。否则报错
类似于C语言中的定义函数
带参数的方法定义
带参数的方法定义
变量名或常量值
注意:
参数的数量与类型必须与方法定义中的设置相匹配,否则报错
形参和实参
传过去的为实参,接收的为形参
带返回值方法的定义和调用
带返回值方法定义
带返回值方法的调用
方法的注意事项
- 方法不能嵌套定义
- void表示无返回值,可以圣罗return,或者后面不跟数据。
方法的通用格式
定义中,要做到两个明确
- 明确返回值类型:主要是明确方法操作完毕后是否有返回值,如果没有写void;如果有,写对应的数据类型
- 明确参数:主要是明确参数的类型和数量
调用方法时:
void类型的方法,直接调用即可
非void类型的方法,推荐用变量接收调用
方法重载
方法重载指同一个类中定义的多个方法之间的关系,满足下列条件的多个方法相互构成重载
- 多个方法在同一个类中
- 多个方法具有相同的方法名
- 多个方法的参数不相同,类型不同或者数量不同
特点:
- 重载仅对应方法的定义,与方法的调用无关,调用方式参照标准格式
- 重载仅仅针对同一个类中方法的名称与参数进行识别,与返回值无关,换句话说不能通过返回值来判定两个方法是否相互构成重载
在调用的时候,Java虚拟机会通过参数的不同来区分同名的方法
ps:我觉得方法重载主要是 功能大致相同以及兼容其余类型的时候使用多个同名方法来构成重载
方法参数传递
基本类型
对于基本数据类型的参数,形式参数的改变,不影响实际参数的值
引用类型
对于引用类型的参数,形式参数的改变,影响实际参数的值
举例数组,数组是以地址存储,为引用类型
Debug
调试工具,它可以用于查看程序的执行流程,也可以追踪程序执行过程来调试程序
又称为断点调试,断点是一个标记,告诉从哪里开始查看
如何加断点
行号后面点击即可 一个红点
如何运行加了断点的程序
代码区右键Debug运行
看哪里
看Debugger窗口,还要看Console窗口
点哪里
按F7,或者点Step Into 步进按钮 图标为一个向下箭头 最后左边方块Stop结束
如何删除断点
选择删除断点,单击即可 多个断也可一键删除
debug调试从断点开始一步一步往下执行,显示执行过程与相应变化
碰见键盘输入值 则需输入否则无法继续运行
导包:
- 手动导包 自己输入
- 快捷键导包 Alt+Enter
- 自动导包 输入的时候有提示就可以自动导入
面向对象基础
类和对象
对象是具体的,类是抽象的
- 类是对象的数据类型
- 类是具有相同属性和行为的一组对象的集合
- 属性:对象具有的各种特征,每个对象的每个属性都拥有特定的值
- 行为:对象能够执行的操作
类的定义
类的重要西行:是java程序的基本组成单位
类的组成:属性和行为
- 属性:在类中通过成员变量来体现(类中方法外的变量)
- 行为:在类中通过成员方法来体现(和前面的方法去掉static关键字即可)
类的定义
范例:
对象的使用
创建对象
- 类名 对象名 = new 类名();
- 范例 Phone p = new Phone();
对象内存图
单个对象内存图
多个对象内存图
创建两个对象地址值不相同 分别对应各自创建的对象地址
多个对象指向相同
多个指向一个地址 其中一个改变 则值都改变 因为不是new出来的新的 都是共用一个 所以修改操作都可以
成员变量和局部变量
区别:
封装
private关键字
为防止数据隐患
可以把数据设置为private 然后public方法来控制设置和读取 设置一些判断数据合格的限制,就可以防止数据隐患 以防止调用方数据错误
private关键字的使用
this关键字
- this修饰的变量用于指代成员变量
- this.name = name 前面是成员变量后面是局部变量
- 方法的形参如果与成员变量同名,不带this修饰的变量指的是形参,而不是成员变量
- 方法的形参没有与成员变量同名,不带this修饰的变量指的是成员变量
什么时候使用this呢?解决局部变量隐藏成员变量 同名
this:代表所在类的对象引用
- 记住:方法被哪个对象调用,this就代表哪个对象
this内存原理
封装
封装概述
是面向对象三大特征之一(封装,继承,多态)
是面向对象编程语言对客观世界的模拟,客观世界里成员变量都是隐藏在对象内部的,外界是无法直接操作的
封装原则
将类的某些信息隐藏在类内部,不允许外部程序直接访问,而是通过这类提供的方法来实现对隐藏信息的操作和访问成员变量private,提供对应的***getXxx()/setXxx()***方法
封装好处
通过方法来控制成员变量的操作,提高了代码的安全性
把代码用方法进行封装,提高了代码的复用性
构造方法
构造方法是一种特殊的方式
作用:创建对象
当调用这种对象的时候 构造方法就会执行
调用s.show(); 那个构造会执行
它跟别的方法不一样 它格式不同而且后面不是定义的方法名 而是类名来当构造方法名
构造方法的注意事项
可以定义多个构造方法 分为无参构造方法和有参构造方法,有参数的在new类的时候输入参数
重载形式
- 构造方法的创建
- 如果没有定义构造方法,系统将给出一个默认的无参数构造方法
- 如果定义了构造方法,系统将不再提供默认的构造方法
- 构造方法的重载
- 弱国自定义了带参构造方法,还要使用无参数构造方法,就必须写一个无参数构造方法
- 推荐的使用方法
- 无论是否使用,都手工书写无参数构造方法
标准类的制作
- 成员变量
- 使用private修饰
- 构造方法
- 提供一个无参构造方法
- 提供一个带多个参数的构造方法
- 成员方法
- 提供每一个成员变量对应的setXxx()/getXxx()
- 提供一个显示对象信息的show()
- 创建对象并因为其成员变量赋值的两种方式
- 无参构造方法创建对象后使用**setXxx()**赋值
- 使用带参构造方法直接创建带有属性值的对象
字符串
API
API概述
API (Application Programming interface):应用程序编程接口
帮助文档的使用
可以查看api用法
API使用练习
查找文档学习Scanner用法,来实现用键盘输入字符串
nextLine(); 写一个这个 然后点 Ctrl + Alt + V 可以生成左式子
Sting
String类在java.lang包下,所以使用的时候不需要导包
String类代表字符串,Java程序中的所有字符串文字(例如”abc“)都被实现为该类的实例 也就是说,Java程序中所有的双引号字符串,都是String类的对象
- 字符串的特定
- 字符串不可变,它们的值在创建后不能更改
- 虽然String的值不可变,但是它们可以被共享
- 字符串效果上相当于字符数组(char[]),但是底层逻辑是字节数组(byte[])
- String的构造方法
字节数组 为字母ASCII码 转换成字符来输出
推荐使用直接赋值的方式得到字符串对象
字符串对象的特点
- 通过new创建的字符串对象,每一次new都会申请一个内存空间,虽然内容相同,但是地址值不同
- 以 ”“ 给出的字符串,只要字符序列相同(顺序和大小写),无论程序代码出现几次,JVM都只会建立一个String对象,并在字符串池中维护
字符串的比较
使用 == 比较
- 基本类型:比较的是数据值是否相同
- 引用类型:比较的是地址值是否相同
字符串比较用equals()方法
s1.equals(s2);
遍历字符串
思路与数组相似
charAt(int index)方法 获取字符串中的每一个字符
length() 获得字符串长度
字符串拼接
字符串反转
帮助文档查看String方法
StringBuilder
StringBuilder 是一个可变的字符串类,我们可以把它看成是一个容器
这里的可变指的是StringBuilder对象中的容器是可变的
String和StringBuilder的区别:
- String:内容是不可变的
- StringBuilder:内容是可变的
StringBuilder构造方法
StringBuilder的添加和反转方法
append();方法
返回来是对象本身就可以再调,链式编程
reverse();方法
直接反转字符串
StringBuilder和String相互转换
toString() StringBuilder转为String
StringBuilder(String s) 构造方法 String转为StringBuilder
通过帮助文档查看StringBuilder中的方法
ArrayList
编程存取多个数据,使用长度固定的数据存储格式,不一定满足需求,
集合类的特点:提供一种存储空间可变的存储模型,存储的数据容量可以发生改变
ArrayList就是其中一个集合类
ArrayLIst < E > :
- 可调整大小的数组实现
- < E > :是一种特殊的数据类型,泛型。
用法:
在 E 的地方使用引用数据类型替换即可
范例:ArrayList < String >, ArrayList < Student >
ArrayList构造方法和添加方法
如何使用:
ArrayList集合常用方法
空集合默认为[]
像指定索引处插入元素,此位置及后面元素后移
存储字符串并遍历
存储学生对象并遍历
存储学生对象并遍历升级版
学生管理系统
Alt+insert 组合 根据自己需要选择 可以生成构造方法 效率
项目操作。
继承
继承是面向对象三大特征之一。可以使得子类具有父类的属性和方法,还可以在子类中重新定义,追加属性和方法。
继承的格式
- 格式:public class 子类名 extends 父类名 { }
- 范例:public class Zi extends Fu { }
- Fu:是父类,也被称为基类、超类
- Zi:是子类,也被成为派生类
继承中子类的特点
- 子类可以有父类的内容
- 子类还可以有自己特有的内容
继承的好处和弊端
继承好处
- 提高了代码的复用性(多个类形同的成员可以放在同一个类中)
- 提高了代码的维护类(如果方法的代码需要修改,修改一处即可)
继承弊端
- 继承让类与类之间产生了关系,类的耦合性增强了,当父类发生变化时子类也不得不跟着变化,削弱了子类的独立性
什么时候使用继承?
- 继承体现的关系:is a
- 假设法:我有两个类A和B,如果他们满足A是B的一种,就说明他们存在继承关系,这个时候就可以考虑继承来体现,否则就不能滥用继承
- 举例:苹果和水果,猫和动物,// 猫和狗 这两个不能说成谁是谁的一种 所以不存在继承关系
继承中变量的访问特点
在子类方法中访问一个变量
- 先在子类局部范围找
- 子类成员范围找
- 父类成员范围找
- 如果都没有就报错(不考虑父亲的父亲…)
super关键字
super可以直接访问父类成员变量。
总结
继承中构造方法的访问特点
子类中所有的构造方法默认都会访问父类中无参的构造方法
问什么呢?
- 因为子类会继承父类中的数据,可能还会使用父类的数据。所以,子类初始化之前,一定要先完成父类数据的初始化
- 每一个子类构造方法的第一句语句默认都是:super()
如果父类中没有无参构造方法,只有带参构造方法,该怎么办?
- 通过使用super关键字去显示的调用父类的带参构造方法
- 在父类中自己提供一个无参构造方法
推荐:自己给出无参构造方法
继承中成员方法的访问特点
通过子类对象访问一个方法
- 子类成员范围找
- 父类成员范围找
- 富国都没有就报错(不考虑父亲的父亲…)
Super内存图
方法重写
概述
- 子类中出现了和父类中一模一样的方法声明
方法重写的应用
- 当子类需要父类的功能,而功能主体子类有自己特有的内容时,可以重写父类中的方法,这样,即沿袭了父类的功能,又定义了子之类特有的内容
重写过程中容易把方法声明写错
为了帮助检验, 在上面写上 @Override 注解 表明下面的方法时重写父类的
当**@Override**报错则下面方法重写有错误
@Override
- 是一个注解(注解后面会学习到)
- 可以帮助我们检查重写方法的方法声明的正确性
方法重写注意事项
- 私有方法不能被重写(父类私有成员 子类是不能继承的)
- 子类方法访问权限不能更低(public > 默认 > private)
Java中继承的注意事项
- Java中类只支支持单继承,不支持多继承
- Java中类支持多层继承
单继承指的是一个类只能继承一个类,多层继承 可以继承的父类再继承一个类
默认无参构造方法,有带参构造方法后,则不执行默认,需要手动创建无参构造方法
修饰符
包
包就是文件夹,方便对类进行分类管理
包的定义格式
- 格式:package包名;(多级包用 . 分开)
- 范例:package com.itheima;
命令提示符 自动创建包的路径 运行时带上包的路径 也可手动建立包
在IDEA左边新建项目 新建模块 在模块下src 新建包 新建java类 然后编译运行 方便
导包
用其余包中的类 需要在前面加上包名 过于繁琐需要每次加包名
或者import 包路径来导入 范例:impot cn.itcaset.Teacher
import java.util.Scanner / Random 也是导包操作
修饰符
状态修饰符
final (最终态)
final关键字是最终的意思,可以修饰成员方法,成员变量,类
final为最终的意思
final修饰的特点:
修饰方法:表明该方法是最终方法,不能被重写
修饰变量:表明该变量是常量,不能再次被赋值
修饰类:表明该类是最终类,不能被继承
final修饰局部变量
static (静态)
static关键字是静态的意思,可以修饰成员方法,成员变量
static修饰的特点
被类的所有对象共享
这也是我们判断是否使用静态关键字的条件可以通过类名调用
当然,也可以通过对象名调用
推荐使用类名调用
static访问特点
非静态的成员方法
静态的成员方法
总结成一句话就是:静态成员方法只能访问静态成员
多态
概述
同一个对象,在不同时刻表现出来的不同状态
举例:猫
多态的前提和体现
- 有继承/实现关系
- 有方法重写
- 有父类引用指向子类对象
多态中成员访问特点
多态的好处和弊端
多态的好处:提高了程序的扩展性
具体体现:定义方法的时候,使用父类型作为参数,将来在使用的时候,使用具体的子类型参与操作
多态的弊端:不能使用子类的特有功能
多态中的转型
向上转型
从子到父
父类引用指向子类对象
向下转型
从父到子
父类引用转为子类对象
多态转型内存图解
抽象类
抽象类概述
在Java中,一个没有方法体的方法应该定义为抽象方法,而类中如果有抽象方法,该类必须定义为抽象类
abstract关键字
abstract关键字来说明抽象类抽象方法
抽象类的特点
抽象类和抽象方法必须使用abstract关键字修饰
public abstract class 类名 {}
public abstract void eat();
抽象类中不一定有抽象方法,有抽象方法的类一定是抽象类
抽象类不能实例化
抽象类如何实例化呢?参照多态的方式,通过子类对象实例化,这叫抽象类多态
抽象类的子类
要么重写抽象类中的所有抽象方法
要么是抽象类
抽象类的成员特点
成员变量
可以是变量
也可以是常量
构造方法
有构造方法,但是不能实例化
那么,构造方法的作用是什么呢? 用于子类访问父类数据的初始化
成员方法
可以有抽象方法:限定子类必须完成某些动作
也可以有非抽象方法:提高代码复用性
接口
接口就是一种公共的规范标准,只要符合规范标准,大家都可以通用
Java中的接口更多的体现在对行为的抽象
接口的特点
接口用关键字interface修饰
public interface 接口名 { }
类实现接口用implements表示
public class 类名 implements 接口名 { }
接口不能实例化
接口如何实例化呢?参照多态的方式,通过实现类对象实例化,这叫接口多态。
多态的形式:具体类多态,抽象类多态,接口多态 (用的多)
多态的前提:有继承或者实现关系;有方法重写;有父(类/接口)引用指向(子/实现)类对象
接口的实现类
要么重写接口中的所有抽象方法
要么是抽象类
接口的成员特点
成员变量
只能是常量
默认修饰符: public static final
构造方法
接口没有构造方法,因为接口主要是对行为进行抽象,是没有具体存在
一个类如果没有父类,默认继承自Object类
成员方法
只能是抽象方法
默认修饰符:public abstract
关于接口中的方法,JDK8和JDK9中有一些新特性,后面有讲解
类和接口的关系
类和类的关系
继承关系,只能单继承,但是可以多层继承
类和接口的关系
实现关系,可以单实现,也可以多实现,还可以在继承一个类的同时实现多个接口
接口和接口的关系
继承关系,可以单继承,也可以多继承
抽象类和接口的区别
成员区别
抽象类 变量,常量;有构造方法;有抽象方法,也有非抽象方法
接口 常量;抽象方法
关系区别
类与类 继承,单继承
类与接口 实现,可以单实现,也可以多实现
接口与接口 继承,单继承,多继承
设计理念区别
抽象类 对类抽象,包括属性,行为
接口 对行为抽象,主要是行为
抽象类是对事物的抽象,而接口是对行为的抽象
形参和返回值
类名作为形参和返回值
- 方法的形参是类名,其实需要的是该类的对象
- 方法的返回值是类名,其实返回的是该类的对象
抽象类名作为形参和返回值
- 方法的形参是抽象类名,其实需要的是该抽象类的子类对象
- 方法的返回值是抽象类名,其实返回的是该抽象类的子类对象
接口名作为形参和返回值
- 方法的形参是接口名,其实需要的是该接口的实现类对象
- 方法的返回值是接口名,其实返回的是该接口的实现类对象
内部类
内部类:就是在一个类中定义一个类。举例:在一个类A的内部定义一个类B,类B就被成为内部类
内部类的访问特点:
- 内部类可以直接访问外部类的成员,包括私有
- 外部类要访问内部类的成员,必须创建对象
成员内部类
- 在类的成员位置:成员内部类
- 在类的局部位置:局部内部类
常用用途可能为下图这种 内部类通常来隐藏不让直接访问 通过间接调用来访问
局部内部类
局部内部类是在方法中定义的类,所以外界是无法直接使用,需要在方法内部创建对象并使用
该类可以直接访问外部类的成员,也可以访问方法内的局部变量
匿名内部类
前提:存在一个类或者接口,这里的类可以是具体类也可以是抽象类
局部内部类的特殊形式所以得写在方法中
可以直接在末尾调用内部的方法。
匿名内部类在开发中的使用
可以避免频繁创建新的类
常用API
Math
java.lang 不用导包 所有类直接或者间接继承Object类
查看帮助文档可以详细查看
Math包含执行基本数字运算的方法
没有构造方法,如何使用类中的成员呢?
看类的成员是否都是静态的,如果是,通过类名就可以直接调用
常用方法
System
System包含几个有用的类字段,它不能被实例化,成员方法我为静态的,通过类名可以直接调用
currentTimeMillis()的两种用法
Object
Obeject是类层次结构的根,每个类都可以将Object作为超类。所有类都直接或者间接的继承自该类
构造方法:public Object()
为什么说子类的构造方法默认访问的是父类的无参构造方法?
因为它们的顶级父类只有无参构造方法
看方法的源码,选中方法,按下Ctrl+B | 选中方法 右键 Go To 选declaration
Object中的toString()方法输入对象地址信息,建议重写此方法 自动生成即可
Alt+insert即可自动生成
Object类的常用方法
Arrays
冒泡排序
Arryas类的概述和常用方法
基本类型包装类
将基本数据类型封装成对象的好处在于可以在对象中定义更多的功能方法操作该数据
常用操作之一:用与基本数据类型与字符串之间的转换
除了 int -> Integer 跟 char -> Character 其余都是首字母大写
Integer
第二个String s 为数值类型的字符串
推荐使用下面两个静态方法 可以不用new一个实例,直接类名调用
int 和 String 的相互转换
基本类型包装类的最常见操作就是:用于基本类型和字符串之间的相互转换
Int 转换为 String
public static String **valueOf(int i)**:返回int参数的字符串表示形式。该方法是String类中的方法
String转换为 int
public static int **parseint(String s)**:将字符串解析为int类型。该方法是interger类中的方法
字符串中数据排序
此程序中用到了字符串的split方法
自动装箱和拆箱
- 装箱:把基本数据类型转换为对应的包装类类型
- 拆箱:把包装类类型转换为对应的基本数据类型
注意注意注意
Data
Data代表了一个特定的时间,精确到毫秒
Data类的常用方法
SimpleDateFormat类
SimpleDateFormat是一个具体的类,用于以区域设置敏感的方法格式化和解析日期。重点学习日期格式化和解析
为了不报错 解析要与设定的格式相同
日期工具类
创建的工具类
Calendar类 日历类
Calendar为某一时刻和一组日历字段之间的转换提供了一些方法,并为操作日历字段提供了一些方法
Calendar提供了一个类方法getInstance用于获取Calendar对象,其日历字段已使用当前日期和时间初始化:
Calendar rightNow = Calendar.getInstance();
Calendar 的常用方法
get add set方法示例
二月天案例
注意 月份从0开始记 所以三月值为2
异常
概述
就是程序出现了不正常的情况
JVM的默认处理方案
异常处理
如果程序出现了问题,我们需要自己来处理,有两种处理方案:
- try … catch …
- throws
try…catch…
- 格式
示例:
e是个对象可以调用方法,e.printStackTracec();这个会把错误信息输出在控制台,与默认方法不同是,不会直接退出程序。
Throuwable
Throuwable的成员方法
示例:
编译时异常和运行时异常的区别
throws
虽然try…catch…可以对异常进行处理,但是并不是所有异常都有权限进行处理,也就是说,有些时候可能出现的异常是我们处理不了的,这时候就需要throws的处理方式
格式:
throws 异常类名;
注意:这个格式时跟在方法的括号后面的
示例:
- 编译时异常必须要进行处理,两种处理方案:try…catch…或者throws,如果采用throws这种方案,将来谁调用谁来处理
- 运行时异常可以不处理,出现问题后,需要我们回来修改代码
throws仅仅为抛出异常 处理异常还需要try…catch..来处理
自定义异常
throw与throws的区别
集合进阶
体系结构
Collection
Collection集合概述
- 是单列集合的顶层接口,它表示一组对象,这些对象也称为的Collection的元素
- JDK不提供此接口的任何直接实现,它提供更具体的子接口(如Set和List)实现
创建Collection集合的对象
- 多态的方式
- 具体的实现类ArrayList
范例:
Collection集合常用方法
Alt+7可以打开类的结构窗口 方便查看类中方法定义等
Collection集合的遍历
Iterator迭代器
Iterator:迭代器,集合的专用遍历方式
- Iterator <E> iterator() :返回此集合中元素的迭代器,通过集合的iterator()方法得到
- 迭代器是通过集合的iterator()方法得到的,所以我们说它是依赖于集合而存在的
Iterator中的常用方法
- E next():返回迭代中的下一个元素
- boolean hasNext():如果迭代具有更多的元素,则返回true
先得到迭代器对象
利用hasNext()来判断是否有元素,然后next()输出
这就是Collection集合的 遍历
集合的使用步骤
List集合
概述
- 有序集合(也称为序列),用户可以精确控制列表中每个元素的插入位置。用户可以通过整数索引访问元素,并搜索列表中的元素
- 与Set集合不同,列表通常允许重复的元素
List集合特点
- 有序:存储和取出的元素顺序一致
- 可重复:存储的元素可以重复
List的特有方法
Collection集合没有 儿子集合ArrayList有
List的两种遍历形式
并发修改异常
Listiterator列表迭代器
Listiterator:列表迭代器
通过List集合的listIterator()方法得到,所以说它是List集合特有的迭代器
用于允许程序员沿任意方向遍历列表的列表迭代器,在迭代期间修改列表,并获取列表中迭代器的当前位置
利用ListInterator 来往集合中添加元素
增强for循环
增强for:简化数组和Collection集合的遍历
- 实现Iterable接口允许其对象成为增强类for语句的目标
- 它是JDK5之后出现的,其内部原理是一个Iterator迭代器
增强for的格式
格式:
范例:
int[] arr = {1,2,3,4,5};
for(int i: arr){
System.out.println(i);
}
范例2:
只是为了遍历则选择增强for 因为这是最方便的遍历
如果操作需要索引,只能选择普通for
数据结构
数据结构是计算机存储、组织数据的方式。是指相互之间存在一种或多种特定关系的数据元素的集合
通常情况下,精心选择的数据结构可以带来更高的运行或者存储效率
常见数据结构
常见数据结构之 栈
先进后出的模型
常见数据结构之 队列
先进先出的模型
常见数据结构之 数组
常见数据结构之 链表
List集合的子类特点
List集合常用子类:ArrayList,LinkedList
- ArrayList:底层数据结构是数组,查询快,增删慢
- LInkedList:底层数据结构是链表,查询慢,增删快
LinkedList集合的特有功能
Set集合
- 不包含重复元素的集合
- 没有带索引的方法,所以不能使用普通for循环遍历
HashSet:对集合的迭代顺序不作任何保证
哈希值
哈希值:是JDK根据对象的地址或者字符串或者数字算出来的int类型的数值
Object类中的一个方法可以获取对象的哈希值
- public int hashCode():返回对象的哈希码值
特点:
同一个对象多次调用hashCode()方法返回的哈希值是相同的
默认情况下,不同对象的哈希值是不相同的
通过方法重写,可以实现不同对象的哈希值是相同的
字符串重写了hashCode 相同
HashSet集合
HashSet集合特点
- 底层数据结构是哈希表
- 对集合的迭代顺序不作任何保证,也就是说不保证存储和取出的元素顺序一致
- 没有带索引的方法,所以不能使用普通的for循环遍历
HashSet集合保证元素唯一性的分析
HashSet集合存储元素
- 要保证元素唯一性,需要重写hashCode()和equals()
常见数据类型之 哈希表
哈希表
- JDK8之前,底层采用数组+链表实现,可以说是一个元素为链表的数组
- JDK8以后,在长度比较长的时候,底层实现了优化
哈希表保证了唯一性 hashCode计算哈希值 哈希表默认16位,取余16然后得到哈希值 然后存入哈希表 保证唯一性过程 先比较哈希值是否相同 如果相同再比较内容是否相同 如果都相同则不存入哈希表
利用HashSet存储学生对象 并保证唯一性 需要重写两个方法
LinkedHashSet集合
哈希表和链表实现的Set接口
概述:
LinkedHashSet集合特点
- 哈希表和链表实现的Set接口,具有可预测的迭代次序
- 由链表保持元素有序,也就是说元素的存储和取出顺序是一致的
- 由哈希表保证元素唯一,也就是说没有没有重复的元素
TreeSet集合
TreeSet集合特点
范例:
继承Set所以没有重复元素 自然排序
自然排序Comparable的使用
- 存储学生对象并遍历,创建TreeSet集合使用无参构造方法
- 要求:按照年龄从小到大排序,年龄相同时,按照姓名的字母顺序排序
结论
- 用TreeSet集合存储自定义对象,无参构造方法使用的是自然排序对元素进行排序的
- 自然排序,就是让元素所属的类实现Comparable接口,重写CompareTo(T o)方法
- 重写方法时,一定要注意排序规则必须按照要求的主要条件和次要条件来写
比较器排序Comparator的使用
- 存储学生对象并遍历,创建TreeSet集合使用带参构造方法
- 要求:按照年龄从小到大排序,年龄相同时,按照姓名的字母顺序排序
结论:
- 用TreeSet集合存储自定义对象,带参构造方法使用的时比较器排序对元素进行排序的
- 比较器排序,就是让集合构造方法接收comparator的实现类对象,重写compare(T o 1,T o 2)方法
- 重写方法时,一定要注意排序规则必须按照要求的主要条件和次要条件来写
泛型
本质是参数化类型
参数化类型就是:将类型由原来的具体的类型参数化,然后在使用/调用时传入具体的类型
可以在类、方法、和接口中,分别成为泛型类、泛型方法、泛型接口
泛型定义格式:
- <类型>:指定一种类型的格式。这里的类型可以看成是形参
- <类型1,类型2…>:指定多种类型的格式,多种类型之间用逗号隔开。这里的类型可以看成是形参
- 将来具体调用时候给定的类型可以看成是实参,并且实参的类型只能是引用数据类型
泛型的好处:
- 把运行使其的问题提前到了编译期间
- 避免了强制类型转换
泛型类
定义和使用
定义
使用
Generic <String> gs1 = new Generic <String> () { }
gs1.setT()这时候参数就是String类型的
泛型方法
泛型接口
泛型接口的定义格式:
- 格式:修饰符 interface 接口名 <类型> { }
泛型接口需要由泛型类来实现
类型通配符
为了表示各种泛型List的父类,可以使用类型通配符
- 类型通配符:<?>
- List<?>:表示元素类型未知的List,它的元素可以匹配任何的类型
- 这种带通配符的List仅表示它是各种泛型List的父类,并不能把元素添加到其中
可变参数
可变参数又称参数个数可变,用作方法的形参出现,那么方法参数个数就是可变的了
- 格式:修饰符 返回值类型 方法名(数据类型… 变量名) { }
- 范例:public static int sum (int… a) { }
a 其实是一个数组
注意事项
- 这里的变量其实是一个数组
- 如果一个方法有多个参数,包含可变参数,可变参数要放在最后
可变参数的使用
Map集合
Map集合概述
- Interface Map<K,V> K:键的类型;V:值得类型
- 将键映射到值得对象;不能包含重复得键;每个可以映射到最多一个值
- 举例:学生的学号和姓名
学号作为键唯一 姓名作为值被学号映射
创建Map集合的对象
- 多态的方式
- 具体的实现类HashMap
put方法用来添加数据 如果键重复则覆盖上一个键的值 也可做修改操作
Map集合的基本功能
Map集合的获取功能
范例:
Map集合的遍历
方式一:
范例:
方式二:
范例:
HashMap存储值为Student遍历练习案例
范例:
HashMap存储键为Student遍历练习案例
重写这两个方法自动生成即可
范例:
最后需要在Student类中重写equals和hashcode方法来确保数据唯一性
//应该是因为中文字符串哈希值相同
ArrayList集合存储HashMap元素并遍历
集合嵌套案例
范例:
HashMap集合存储ArrayList元素并遍历
范例:
统计字符串中每个字符出现的次数
范例:
String s = sb.toStrint();
System.out.println(“s”);
可以用TreeMap改进,从而达到按照键自然排序
Collections
- 针对集合操作的工具类
Collection是一个接口,Collections是一个具体的类。
此类仅用静态方法组成,它包含对集合进行操作的算法 用类名直接直接调用方法即可
Collections类的常用方法
范例:
ArrayList存储学生对象并排序
模拟斗地主
模拟斗地主升级版
IO流
File
File:它是啥文件和目录路径名的抽象表示
文件和目录是可以通过File封装成对象的
对于File而言,其封装的并不是一个真正存在的文件,仅仅是一个路径名而已。它可以是存在的,也可以是不存在的。
将来时要通过具体的操作把这个路径的内容转换为具体存在的
构造方法:
范例:
三个输出相同
File类创建功能
范例:
同一个文件夹下创建同名不能成功,不能看名字来判断是否是文件,得看具体方法,如果方法用错了文件夹也可以 是文件的名称
File类判断和获取功能
范例:
FIle类的删除功能
空目录才能直接删除
创建文件的时候,找不到目录回报错
删除目录时候的注意事项:
- 如果一个目录中有内容(目录,文件),不能直接删除。应该 先删除目录中的内容,最后才能删除目录
递归
编程角度看,递归指的是方法定义中调用方法本身的现象
递归需要出口否则就回StackOverflowError:堆栈溢出,递归太深
把一个负责的问题层层转化为一个与原问题相似的规模较小的问题来求解
递归策略只需少量的程序就课描述出解题过程所需要的多次重复计算
递归解决问题要找到两个内容:
- 递归出口:否则会出现内存溢出
- 递归规则:与原问题相似的规模较小的问题
遍历目录
IO流描述
IO:输入/输出(input/output)
流:是一种抽象概念,是对数据传输的总称。也就是说数据在设备间的传输称为流,流的本质是数据传输
IO流就是用来处理设备间数据传输问题的
常见的应用:文件复制;文件上传;文件下载
IO流分类:
按照数据的流向
输入流:读数据
输出流:写数据
按照数据类型来分
字节流
字节输入流;字节输出流
字符流
字符输入流;字符输出流
一般来说,我们说IO流的分类是按照数据类型来分的
那么这两种流都在什么情况下使用呢?
- 如果数据通过Window自带的记事本软件打开,我们能读懂里面的内容,就使用字符流,否则使用字节流。如果你不知道该使用哪种类型的流,就使用字节流。
字节流
字节流写数据
跟IO相关基本都在Java.io包下需要导包
字节流抽象基类
InputStream 类 这个抽象类是表示输入字节流的所有类的超类
OutputStream类 这个抽象类是表示字节输出流的所有类的超类
子类名特点:子类名称都是以其父类名作为子类名的后缀
FileOutputStream输出流
FileOutputStream:文件输出流用于将数据写入File
- FIleOutputStream(String name):创建文件输出流以指定的名称写入文件
做了三件事情:
- 调用系统功能创建了文件
- 创建了字节输出流对象
- 让字节输出流对象指向创建好的文件
使用字节输出流写数据的步骤:
- 创建字节输出流对象(调用系统功能创建了文件,创建字节输出流对象,让字节输出流对象指向文件)
- 调用字节输出流对象的写数据方法 write 97代表a
- 释放资源(关闭此文件输出流并释放与此流相关联的任何系统资源)
字节流写数据的三种方式
范例:
字节流写数据的两个问题
字节流写数据如何实现换行?
写完数据后,加换行符
windows:\r\n
linux:\n
mac:\r
字节流写数据如何实现追加写入?
- publicFileOutputStream(String name,boolean append)
- 创建文件输出流以指定的名称写入文件。
字节流写数据加异常处理
finally:在异常处理时提供finally块来执行所有清楚操作。比如说IO流中的释放资源
特点:被finally控制的语句一定会执行,除非JVM退出
格式用法:
健壮的程序 必要的判断需要写
字节流读数据
FileInputStream
File Input Stream:从文件系统中的文件获取输入字节
- FileInputStream(String name):通过打开与实际文件的连接来创建一个FileInputStream,该文件由文件系统中的路径名 name命名
使用字节输入流读数据的步骤
- 创建字节输入流对象
- 调用字节输入流对象的读数据方法
- 释放资源
字节流读数据的标准代码(一次读一个字节)
int by;
while ((by = finput.read()) != -1) {
System.out.print((char) by)
}
复制文本文件
一次读一个字节复制操作
字节流读数据 字节数组
复制图片
字节缓冲流
- BufferOutputStream:该类实现缓冲输出流。通过设置这样的输出流,应用程序可以向底层输出流写入字节,而不必为写入的每个字节导致底层系统的调用
- BufferedInputStream:创建BuferedInputStream将创建一个内部缓冲区数组。当从流中读取或跳过字节时,内部缓冲区将根据需要从所包含的输入流中重新填充,一次很多字节
构造方法:
- 字节缓冲输出流:BUfferedOutputStream(Output Stream out)
- 字节缓冲输入流:BUfferedInputStream(InputStream in)
为什么构造方法需要的时字节流,而不是具体的文件或者路径呢?
- 字节缓冲流仅仅提供缓冲区,而整整的读写数据还得依靠基本的字节流对象进行操作
范例:
复制视频四种案例 字节缓冲流,一次读写一个字节数组 最快
字符流
一个汉字存储:
如果是GBK编码,占用2个字节
如果是UTF-8编码,占用3个字节
由于字节流操作中文不是特别的方便,所以Java就提供了字符流
- 字符流 = 字节流 + 编码表
用字节流复制文本文件是,文本文件也会有中文,但是没有问题,原因是最终底层操作会自动进行字节拼接成中文,如何识别是中文的呢?
- 汉字在存储的时候,无论选择哪种编码存储,第一个字节都是负数
编码表
基础知识:
计算机储存信息都是二进制数表示;我们在屏幕上看到的英文、汉字字符是二进制数转换之后的结果
按照某种规则,将字符存储导计算机中,称为编码。反之,将存储在计算机中二进制数按照某种规则解析显示出来,称为解码。这里强调一下:按照A的编码存储,必须按照A编码解析,这样才能显示正确符号。否则就会导致乱码现象
字符编码:就是一套自然语言的字符与二进制数之间的对应规则(A,65)
字符集:
是一个系统支持的所有字符的集合,包括各国家文字、标点符号、图形符号、数字等
计算机要准确的存储和识别各种字符集符号,就需要进行字符编码,一套字符集必然至少有一套字符编码
常见字符集有ASCII字符集、GBXXX字符集、Unicode字符集等
字符串中编码解码问题
使用何种方式编码,就要使用何种方式解码,否则会乱码
字符流中的编码解码问题
字符流抽象基类
- Reader:字符输入流的抽象类
- Writer:字符输出流的抽象类
字符流中和编码解码问题相关的两个类
- InputStreamReader
- OutputStreamWriter
范例:
字符流写数据的5种方法
了解flush()和close()方法的区别
字符流读数据的2种方式
遍历读取
先定义
一次读一个字符数据
一次读一个字符数组数据
两种简单的方法
遍历读取写入与上相同
字符缓冲流
字符缓冲流:
- BufferedWriter:将文本写入字符输出流,缓冲字符,以提供单个字符,数组和字符串的高效写入,可以指定缓冲区大小,或者可以接受默认大小。默认值足够大,可用于大多数用途
- BufferedReader:从字符输入流读取文本,缓冲字符,以提供字符,数组和行的高效读取,可以指定缓冲区大小,或者可以使用默认大小。默认值足够大,可用于大多数用途
构造方法:
- BufferedWriter(Writer out)
- BufferedReader(Reader in)
参数用FileReader或者OutStreamWriter都可以
字符缓冲流复制文件 这是其中一种方法用的字符数组
BufferedReader br = new BufferedReader(new FileReader("file.txt"));
BufferedWriter bw = new BufferedWriter(new FileWriter("filecopy2.txt"));
//
char[] ch = new char[1024];
int len;
while((len = br.read(ch))!=-1){
bw.write(ch,0,len);
}
br.close();
bw.close();
字符缓冲流特有功能
BufferedWriter:
- void newLine(): 写一行行分隔符,行分隔符字符串由系统属性定义
BufferedReader:
- public String readLine(): 读一行文字。结果包含行的内容的字符串,不包括任何行终止字符,如果流的结尾已经到达,则为null
改进循环读取
改进复制文件 最常用 优先掌握
BufferedReader br = new BufferedReader(new FileReader("file.txt"));
BufferedWriter bw = new BufferedWriter(new FileWriter("filecopy3.txt"));
//遍历
String st;
while((st = br.readLine())!=null){
bw.write(st);
bw.newLine();
bw.flush();
}
br.close();
bw.close();
IO流小结
字节流小结
- 字节流可以复制任意文件数据,有4种方式一般采用字节缓冲流一次读写一个字节数组的方式
字符流小结
- 字符流只能复制文本数据,有5种方式,一般采用字符缓冲流的特有功能
集合到文件案例
文件到集合案例
案例:点名器
集合到文件改进版
文件到集合改进版
split分割 按照指定字符分割字符串 赋给字符串数组
集合到文件 数据排序进阶版
案例:复制单级文件夹
需求:
代码示例:
public class CopyDir {
public static void main(String[] args) throws IOException {
File fl1 = new File("C:\\Users\\李嘉欣\\Desktop\\CopyDemo");
String name = fl1.getName();
File fl2 = new File(name);
if(!fl2.exists()){
fl2.mkdir();
}
File[] files = fl1.listFiles();
for(File flt:files){
String name1 = flt.getName();
File fl4 = new File(fl2, name1);
methos1(flt,fl4);
}
}
private static void methos1(File flt, File fl4) throws IOException {
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(flt));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(fl4));
byte[] bytes = new byte[1024];
int len;
while((len = bis.read(bytes))!=-1){
bos.write(bytes,0,len);
}
bis.close();
bos.close();
}
}
复制多级文件夹
代码示例:
public class CopyFolederDemo {
public static void main (String[] args) throws IOException{
File srcFile = new File("C:\\Users\\李嘉欣\\Desktop\\CopyDemo");
File destFile = new File(".");
//
copyFolder(srcFile,destFile);
}
private static void copyFolder(File srcFile, File destFile) throws IOException {
if(srcFile.isDirectory()){
String srcFileName = srcFile.getName();
File newFloder = new File(destFile,srcFileName);
if(!newFloder.exists()){
newFloder.mkdir();
}
File[] fileArray = srcFile.listFiles();
for(File file:fileArray){
copyFolder(file,newFloder);
}
}else {
File newFile = new File(destFile,srcFile.getName());
copyFile(srcFile,newFile);
}
}
public static void copyFile(File srcFile, File destFile) throws IOException {
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(srcFile));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(destFile));
byte[] bys = new byte[1024];
int len;
while ((len = bis.read(bys))!=-1){
bos.write(bys,0,len);
}
bis.close();
bos.close();
}
}
复制文件的异常处理
标准处理流程:
JDK7的改进方案: 用的更多点
JDK9的改进方案 仍需抛出
特殊操作流
标准输入输出流
标准输入流
标准输出流
打印流
打印流分类:
- 字节打印流:PrintStream
- 字符打印流:Printwriter
打印流的特点:
- 只负责输出数据,不负责读取数据
- 有自己的特有方法
字节打印流
PrintStream(String fileName):使用指定的文件名创建新的打印流
使用继承父类的方法写数据,查看的时候会转码;使用自己的特有方法写数据,查看的的数据原样输出
字符打印流
创建字符打印流时,如果需要自动刷新流
然后print println方法就会自动刷新
复制java文件打印流改版
对象序列化流
对象序列化:就是将对象保存到磁盘中,或者在网络中传输对象
这种机制就是使用一个字节序列表示一个对象,该字节序列包含:对象的类型、对象的数据和对象中存储的属性等信息
字节序列写到文件之后,相当于文件中持久保存了一个对象信息
反之,该字节序列还可以从文件中读取回来,重构对象,对它进行反序列化
要实现序列化和反序列化就要使用对象序列化流和对象反序列化流:
- 对象序列化流:ObjectOutputStream
- 对象反序列化流:ObjectInputStream
使用序列化对象 对象需要实现Serializable接口 此接口只是一个标识接口 没有抽象方法 不需要重写方法
看到此接口 就要知道此类可以被序列化和反序列化
对象序列化流 示例:
注意事项:
- 一个对象要想被序列化,该对象所属的类必须实现Serializable接口
- Serializable是一个标记接口,实现该接口,不需要重写任何方法
对象反序列化流
反序列化先前使用ObjectOutputStream编写的原始数据和对象
反序列化流的使用
序列化异常修改与不想被序列化
serialVersionUID&transient 问题
Properties
Properties作为Map集合的使用 Properties没有指定泛型
Properties作为集合的特有方法:
示例:
Properties和IO流结合的方法:
结合示例:
myLoad 文件中数据加载到集合
myStore集合中的数据保存到文件
案例:游戏次数
进程和线程
进程:是正在运行的程序
- 是系统进行资源分配和调用的独立单位
- 每一个进程都有它自己的内存空间和系统资源
线程:是进程中的单个顺序控制流,是一条执行路径
- 单线程:一个进程如果只有一条执行路径,则称为单线程程序
- 多线程:一个进程如果有多条执行路径,则称为多线程程序
举例:
- 记事本程序 单线程 同时只能执行一个
- 扫雷程序 多线程 最少两个线程在同时执行 边扫雷 边计时
多线程的的实现方式1 继承Thread类
方式1:继承Thread类
- 定义一个类MyThread继承Thread类
- 在MyThread类中重写run() 方法
- 创建MyThread类的对象
- 启动线程
两个问题:
为什么重写run() 方法?
因为run() 是用来封装被线程执行的代码
run() 方法 和 start() 方法的区别?
run() :封装线程执行的代码,直接调用,相当于普通方法的调用
start():启动线程;然后由JVM调用此线程的run() 方法
设置和获取线程名称
Thread类中设置和获取线程名称的方法
- void setName(String name):将此线程的名称更改为等于参数name
- String getName():返回此线程的名称
无参构造会给赋一个默认的线程名称 Thread-0 开始
使用setName更改 通过getName获取
带参构造方法也可以设置线程名称
不过得在线程类中 设置无参和带参构造方法用来把参数传给Thread 继承Thread类
Thread类中 静态的方法currentThread() 返回对当前正在执行的线程对象的引用
示例: 返回main线程的名称
main方法线程名为main
总结:
线程调度
线程有两种调度模型
- 分时调度模型:所有线程轮流使用CPU的使用权,平均分配每个线程占用CPU的时间片
- 抢占式调度模型:优先让优先级高的线程使用CPU,如果线程的优先级相同,那么就会随机选择一个,优先级高的线程获取的CPU时间片相对多一些
Java使用的是抢占式调度模型
加入计算机只有一个CPU,那么CPU在某一个时刻只能执行一条指令,线程只有得到CPU时间片,也就是使用权,才可以执行指令。所以说多线程程序的执行是有随机性,因为谁抢到了CPU使用权是不一定的
Thread类中设置和获取线程优先级的方法
- public final int **getPriority()**:返回此线程的优先级
- public final void **setPriority(int newPriority)**:更改此线程的优先级
线程默认优先级是5;线程优先级的范围是:1-10
线程优先级高仅仅表示线程获取的CPU时间片的几率高,但是要在次数比较多,或者多次运行的时候才能看到你想要的效果
线程控制
线程生命周期
多线程的实现方式2 实现Runnable接口
方式2:实现Runnable接口
定义一个类MyRuannbale实现Runnable接口
在MyRunnable类中重写run() 方法
创建MyRunnable类的对象
创建Thread类的对象,把MyRunnable对象作为构造方法的参数
启动线程
多线程的实现方案有两种
- 继承Thread类
- 实现Runnable接口
相比继承Thread类,实现Runnable接口的好处
- 避免了Java单继承的局限性
- 适合多个相同程序的代码去处理同一个资源的情况,把线程和程序的代码、数据有效分离,较好的体现了面向对象的设计思想
线程同步
案例:卖票
卖票出现了我呢提
- 相同的票出现了多次
- 出现了负数的票
问题原因:
- 线程执行的随机性导致的
卖票案例数据安全问题的解决
为什么出现问题?(这也是我们判断多线程程序是否有数据安全问题的标准)
- 是否是多线程环境
- 是否有共享数据
- 是否有多条语句操作共享数据
三个问题都有肯定会出现数据安全问题
如何解决多线程安全问题呢?
- 基本思想:让程序没有安全问题的环境
怎么实现呢?
把多条语句操作共享数据的代码锁起来,让任意时刻只能有一个线程执行即可
Java提供了同步代码块的方式来解决
同步代码块:
锁多条语句操作共享数据,可以使用同步代码块实现
格式:
synchronized(任意对象){ 多条语句操作共享数据的代码 }
synchronized(任意对象):就相当于给代码加锁了,任意对象就可以看成是一把锁
同步的好处和弊端
- 好处:解决了多线程的数据安全问题
- 弊端:当线程很多时,因为每个线程都会去判断同步上的锁,这是很耗费资源的,无形中会降低程序的运行效率
同步代码块解决卖票问题:
同步方法:
同步方法:就是把synchronized关键字加到方法上
格式:
修饰符synchronizedf返回值类型 方法名(方法参数) { }
同步方法的锁对象是什么呢?
- this
同步静态方法的锁对象是什么呢?
- 类名.class
线程安全的类
StringBuffer 这个会用
- 线程安全,可变的字符序列
- 从JDK5开始,被StringBuilder替代。通常应该使用StringBuilder类,因为它支持所有相同的操作,单它更快,因为它不执行同步
Vector 已被Collections里面的静态方法synchronizedList(List<T> List)替代
- 从Java2平台v1.2开始,该类改进了List接口,使其成为了Java Collections Farmwork的成员。与新的集合不同,Vector被同步。如果不需要线程安全的实现,建议使用ArrayList代替Vector
Hashtable 已被Collections里面的静态方法synchronizedMap(Map<K,V> m)替代
该类实现了一个哈希表,它将键映射到值。任何非null对象都可以用作键或者值
从Java 2平台v1.2开始,该类进行了改进,实现了Map接口,使其成为Java Collections Farmwork的成员。与新的集合实现不同。Hashtable被同步。如果不需要线程安全的实现,建议使用HashMap代替Hashtable
还有Collections里面的静态方法synchronizedSet(Set<T> s)
Lock锁
Lock实现提供比使用synchronized方法和语句可以获得更广泛的锁定操作
Lock中提供了获得锁和释放锁的方法
- void lock():获得锁
- void unlock():释放锁
Lock是接口不能直接实例化,这里采用它的实现类ReentrantLock来实例化
ReentrantLock的构造方法
- ReentranLock():创建一个ReentrantLock的实例
示例:
生产者消费者
生产者消费者模式是一个非常经典的多线程协作的模式,弄懂生产者消费者问题能够让让我们对多线程编程的理解更加深刻
所谓的生产者消费者问题,实际上主要是包含了两类线程:
- 一类是生产者线程用于生产数据
- 一类是消费者线程用于消费数据
为了解耦生产者和消费者的关系,通常会采用共享的数据区域,就像是一个仓库
- 生产者生产数据之后直接放置在共享数据区中,并不需要关心消费者的行为
生产者消费者案例
等待wait() 唤醒notifyAll() notify()
网络编程
计算机网络:
- 是指将地理位置不同的具有独立功能的多台计算机及其外部设备,通过通信线路连接起来,在网络操作系统,网络管理软件及网络通信协议的管理和协调下、实现资源共享和信息传递的计算机系统
网络编程
- 在 网络通信协议下, 实现网络互连的不同计算机上运行的程序之间可以进行数据交换
网络编程三要素
IP地址
端口
协议
IP地址
IP地址:是网络中设备的唯一标识
IP地址分为两大类
常用命令:
- ipconfig:查看本机IP地址
- ping IP地址:检查网络是否连通
特殊IP地址:
- 127.0.0.1:回送地址,可以代表本机地址,一般用来测试
InetAddress的使用
为了方便我们对IP地址的获取和操作,Java提供了一个类InetAddress供我们使用
InetAddress:此类表示Internet协议 (IP) 地址
示例:
端口
端口:设备上应用程序的唯一标识
端口号:用两个字节表示的整数,它的取值范围是065535。其中,01023之间的端口号用于一些知名的网络服务和应用,普通的应用程序需要使用1024以上的端口号。如果端口号被另外一个服务或应用所占用,会导致当前程序启动失败
协议
协议:计算机网络中,连接和通信的规则被称为网络通信协议
UDP协议
- 用户数据报协议(User Datagram Protocol)
- UDP是无连接通信协议,即在数据传输时,数据的发送端和接收端不建立逻辑连接。简单来说,当一台计算机向另外一台计算机发送数据时,发送端不会确定接收端是否存在,就会发出数据,同样接收端在收到数据时,也不会向发送端反馈是否收到数据。
由于使用UDP协议消耗资源小,通信效率高,所以通常都会用于音频、视频和普通数据的传输
TCP协议
传输控制协议(Transmission Control Procol)
TCP协议是面向连接的通信协议,即传输数据之前,在发送端和接收端建立逻辑连接,然后再传输数据,它提供了两台计算机之间可靠无差错的数据传输。在TCP连接中必须要明确客户端与服务器端,由客户端向服务端发出连接请求,每次连接的创建都需要经过 ”三次握手“
三次握手:TCP协议中,在发送数据的准备阶段,客户端与服务器之间的三次交互,以保证连接的可靠
第一次握手,客户端向服务器端发出连接请求,等待服务器确认
第二次握手,服务器端向客户端回送一个响应,通知客户端收到了连接请求
第三次握手,客户端再次向服务器端发送确认信息,确认连接
- 完成三次握手,连接建立后,客户端和服务器就可以进行数据传输了。由于这种面向连接的特性,TCP协议可以保证传输数据的安全,所以应用十分广泛。例如上传文件、下载文件、浏览网页等
UDP通信程序
UDP协议是一种不可靠的网络协议,它在通信的两端各建立一个Socket对象,但是这两个Socket只是发送,接受数据的对象
因此对于基于UDP协议的通信双方而言,没有所谓的客户端和服务器的概念
Java提供了DatagramSocket类作为基于UDP协议的Socket
UDP发送数据
发送数据的步骤
创建发射端的Socket对象(DatagramSocket)
DatagramSocket()
创建数据,并把数据打包
DatagramPacket(byte[] buf,int length, Inetaddress address,int port)
调用DatagramSocket对象的方法发送数据
void send(DatagramPacket p)
关闭发送端
void close()
范例:
UDP接收数据
接收数据的步骤
创建接收端的Socket对象(DatagramScoket)
DatagramSocket(int port)
创建一个数据包,用于接收数据
DatagramPacket(byte[] buf, int length)
调用DatagramSocket对象的方法接收数据
void receive(DatagramPacket p)
解析数据包,并把数据在控制台显示
byte[] getdata()
int getLength()
关闭接收端
void close()
范例:
TCP通信程序
TCP通信原理
TCP通信协议是一种可靠的网络协议,它在通信的两端各建立一个Socket对象,从而在通信的两端形成网络虚拟链路,一旦建立了虚拟的网络链路,两端的程序就可以通过虚拟链路进行通信
Java对基于TCP协议的网络提供了良好的封装,使用Socket对象来代表两端的通信端口,并通过Socket产生IO流来进行网络通信
Java为客户端提供了Socket类,为服务器端提供了ServerSocket类
TCP发送数据
发送数据的步骤
创建客户端的Socket对象(Socket)
Socket (String host,int port)
获取输出流,写数据
OutputStream getOutputStream()释放资源
void close( )
范例:
TCP接收数据
接收数据的步骤
创建服务器端的Socket对象(ServerSocket)
ServerSocket(int port)
监听客户端连接,返回一个Socket对象
Socket accept( )
获取输入流,读数据,并把数据显示再控制台
InputStream getInputStream( )
释放资源
void close( )
案例:
范例练习:
服务器端改写版
客户端改写版
集合IO流写入文本文件 接收端改进
在读取文件写入文件 服务器反馈练习中
出现一个问题,服务器端一直等待接收数据
原因:读数据的方法是堵塞式的
解决方法:自定义结束标记;使用shutdownOutput( )推荐
多线程改进问题中 需要不同的文件名
解决办法:
实现Runnable接口 重写run方法 来开启线程
Lambda表达式
函数式编程思想概述
λ 这是lambda符号
在数学中函数就是有输入量、输出量的一套计算方案,也就是 “拿数据做操作”
面向对象思想强调 “必须通过对象的形式来做事情”
函数式思想则尽量忽略面向对象的复杂语法: “强调做什么,而不是以什么形式去做”
我们要学的Lambda表达式就是函数式思想的体现
Lambda的标准格式
组成Lambda表达式的三要素:形式参数,箭头,代码块
区别:
格式:
- 格式:**(形式参数) -> {代码块}**
- 形式参数:如果有多个参数,参数之间用逗号隔开;如果没有参数,留空即可
- -> :由英文中画线和大于符号组成,固定写法。代表指向动作
- 代码块:是我们具体要做的事情,也就是以前我们写的方法体内容
Lambda表达式的练习
Lambda表达式的使用前提
- 有一个接口
- 有且仅有一个抽象方法
练习123
Lambda表达式的省略模式
省略规则:
- 参数类型可以省略。但是多个参数的情况下,不能只省略一个
- 如果参数有且仅有一个,那么小括号可以省略
- 如果代码块的语句只有一条,可以省略大括号和分号,甚至是return
Lambda表达式的注意事项
注意事项:
使用Lambda必须要有接口,并且要求接口中有且仅有一个抽象方法
必须有上下文环境,才能推导出Lambda对应的接口
根据局部变量的赋值得知Lambda对应的接口:Runnable r = () -> System.out.println(“Lambda表达式”);
根据调用方法的参数得知Lambda对应的接口:new Thread( () -> System.out.println(“Lambda表达式”)).start();
Lambda表达式和匿名内部类的区别
所需类型不同
- 匿名内部类:可以是接口,也可以是抽象类,还可以是具体类
- Lambda表达式:只能是接口
使用限制不同
- 如果接口中有且仅有一个抽象方法,可以使用Lambda表达式,也可以使用匿名内部类
- 如果接口中多于一个抽象方法,只能使用匿名内部类,而不能使用Lambda表达式
实现原理不同
- 匿名内部类:编译之后,产生一个单独的.class字节码文件
- Lambda表达式:编译之后,没有一个单独的.class字节码文件。对应的字节码文件会在运行的时候动态生成
接口组成更新
接口组成更新概述
接口的组成
常量
pubic static final
抽象方法
public abstract
默认方法(Java 8)
静态方法(Java 8)
私有方法(Java 9)
接口中的默认方法
接口中默认方法的定义格式:
- 格式:public default 返回值类型 方法名(参数列表) { }
- 范例:public default void show3( ) { }
接口中默认方法的注意事项:
- 默认方法不是抽象方法,所以不强制重写。但是可以被重写,重写的时候去掉default关键字
- public可以省略,default不能省略
接口中的静态方法
接口中静态方法的定义格式:
- 格式:public static 返回值类型 方法名(参数列表) { }
- 范例:public static void show( ) { }
接口中静态方法的在注意事项:
- 静态方法只能通过接口名调用,不能通过实现类名或者对象名调用
- public可以省略,static不能省略
接口中私有方法
接口中私有方法的定义格式:
格式1:private 返回值类型 方法名(参数列表) { }
范例1:private void show() { }
格式2:private static 返回值类型 方法名(参数列表) { }
范例2:private static void method() { }
接口中私有方法的注意事项:
默认方法可以调用私有的静态方法和非静态方法
静态方法只能调用私有的静态方法
方法引用
方法引用符
方法引用符
- :: 该符号为引用运算符,而它所在的表达式被称为方法引用
回顾一下体验方法中的代码
Lambda 表达式 : usePrintable(s -> System.out.println(s));
分析:拿到参数s之后通过Lambda表达式。传递给System.out.println方法去处理
方法引用:usePrintable(System.out :: println);
分析:直接使用System.out中的println方法来取代lambda,代码更加简介
推导与省略
- 如果使用Lambda,那么根据 “可推导就是可省略” 的原则,无需指定参数类型,也无需指定的重载形式,它们都将被自动推导
- 如果使用方法引用,也是同样可以根据上下文进行推导
- 方法引用是Lambda的孪生兄弟
Lambda表达式支持的方法引用
常见的引用方式:
- 引用类方法
- 引用对象的实例方法
- 引用类的实例方法
- 引用构造器
引用类方法
引用类方法,其实就是引用类的静态方法
格式:类名 :: 静态方法
范例:Integer::parselnt
Integer类的方法:public static int parseInt(String s)将此String转换为int类型数据
示例:
引用对象的实例方法
引用对象的实例方法,其实就引用类中的成员方法
- 格式:对象::成员方法
- 范例:”HelloWorld”::toUpperCase
示例:
引用类的实例方法
引用类的实例方法,其实就是引用类中的成员方法
格式:类名::成员方法
范例:String::substring
String类中的方法:public String substring(int beginIndex,int endIndex)
从beginIndex开始到endIndex结束,截取字符串。返回一个子串,子串的长度为endIndex-beginIndex
示例:
引用构造器
引用构造器,其实就是引用构造方法
- 格式:类名::new
- 范例:Student::new
示例:
函数式接口
函数式接口:有且仅有一个抽象方法的接口
Java中的函数式编程体现就是Lambda表达式,所以函数式接口就是可以适用于Lambda使用的接口
只有确保接口中有且仅有一个抽象方法,Java中的Lambda才能顺利的进行推导
如何检测一个接口是不是函数式接口呢?
- @FunctionalInterface
- 放在接口定义的上方:如果接口是函数式接口,编译通过;如果不是,编译失败
注意
- 我们自己定义函数式接口的时候,@FunctionalInterface是可选的,就算我不写这个注解,只要保证满足函数式接口定义的条件,也照样是函数式接口,但是,建议加上该注解
函数式接口作为方法的参数
函数式接口作为方法的返回值
示例:
常用的函数式接口
Java 8在Java.util.function包下预定义了大量的函数式接口供我们使用
重点学习下面4个接口
- Supplier接口
- Consumer接口
- Predicate接口
- Function接口
Supplier接口
Supplier<T>:包含一个无参的方法
- T get():获得结果
- 该方法不需要参数,它会按照某种实现逻辑(由Lambda表达式实现)返回一个数据
- Supplier<T> 接口也被称为生产型接口,如果我们指定了接口的泛型是什么类型,那么接口中的get方法就会生产什么类型的数据供我们使用
使用示例:
示例2:
Consumer接口
COnsumer<T>:包含两个方法
- void accept(T t):对给定的参数执行此操作
- default Consumer <T> andThen (Consumer after):返回一个组合的Consumer,一次执行此操作,然后执行after操作
- Consumer <T> 接口也被称为消费型接口,它消费的数据的数据类型由泛型指定
使用示例:
打印示例:
Predicate接口
使用示例:
and or 的使用
筛选满足条件的集合
Function接口
Function<T,R>:常用的两个方法
- R apply(T t):将此函数应用于给定的参数
- default <V> Function andThen(Function after):返回一个组合函数,首先将该函数应用于输入,然后after函数应用于结果
- Function<T,R>接口通常用于对参数进行处理,转换(处理逻辑由Lambda表达式实现),然后返回一个新的值
示例:
示例:
Stream流
Stream流的操作
Stream流的使用
生成流
通过数据源(集合,数组等)生成流
list.stream()
中间操作
一个流后面可以跟随零个或多个中间操作,其目的主要是打开流,做出某种程度的数据过滤/映射,然后返回一个新的流,交给下一个操作使用
filter( )
终结操作
一个流只能有一个终结操作,当这个操作执行后,流就被使用”光”了,无法再被操作。所以这必定是流的最后一个操作
forEach( )
Stream流的常见生成方式
Collection体系的集合可以使用默认方法stream() 生成流
default Stream <E> stream()
Map体系的集合间接的生成流
数组可以通过Stream接口的静态方法**of(T…values)**生成流
三种生成方式示例:
Stream流的常见中间操作方法
Stream<T> filter(Predicate predicate):用于对流中的数据进行过滤
Predicate接口中的方法
boolean test(T t):对给定的参数进行判断,返回一个布尔值
filter使用示例:
- Stream<T> limit(long maxSize):返回此流中的元素组成的流,截取前指定参数个数的数据
- Stream<T> skip(long n):跳过指定参数个数的数据,返回由该流的剩余元素组成的流
limit和skip的使用示例:
- Static <T> Stream <T> concat (Stream a,Stream b):合并a和b两个流为一个流
- Stream<T> distinct():返回由该流的不同元素(根据Object.equals(Object))组成的流
concat和distinct的使用示例:
- Stream <T> sorted():返回由此流的元素组成的流,根据自然顺序排序
- Stream <T> **sorted (Comparator comparator)**:返回由该流的元素组成的流,根据提供的Comparator进行排序
sort排序示例:
<R> Stream <R> map(Function mapper):返回由给定函数应用于此流的元素的结果组成的流
Function接口中的方法 R apply(T t)
IntStream mapToInt(ToIntFunction mapper):返回一个IntStream其中包含将给定函数应用于此流的元素的结果
IntStream:表示原始int流
ToIntFunction接口中的方法 int applyAsInt(T value)
map和mapToInt方法的使用
示例:
mapToInt 返回类型为IntStream 此类中有一个sum方法比较实用
示例:
Stream流的常见终结操作
常见的:
void forEach(Consumber action):对此流的每个元素执行操作
Consumer接口中的方法 void accept(T t):对给定的参数执行此操作
long count():返回此流中的元素数
forEach和count方法示例:
综合练习
Stream流的收集操作
对数据使用Stream流的方式操作完毕后,我想把流中的数据收集到集合中,该怎么办呢?
Stream流的收集方法
- R collect(Collector collector)
- 但是这个收集方法的参数是一个Collector接口
工具类Collectors提供了具体的收集方式
- public static<T> Collector **toList()**:把元素收集到List集合中
- public static<T> Collector **toSet()**:把元素收集到Set集合中
- public static Collector toMap(Function keyMapper,Function valueMapper):把元素收集到Map集合中
使用范例:
反射
类加载流程
当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过类的加载,类的连接,类的初始化这三个步骤来对类进行初始化。如果不出现意外情况,JVM将会连续完成这三个步骤,所以有时也把这三个步骤统称为类加载或者类初始化
类的加载:
- 就是指将class文件读入内存并为之创建一个java.lang.Class对象
- 任何类被使用时,系统都会为之建立一个java.lang.Class对象
类的连接
- 验证阶段:用于检验被加载的类是否有正确的内部结构,并和其他类协调一致
- 准备阶段:负责为类的类变量分配内存,并设置默认初始化值
- 解析阶段:将类的二进制数据中的符号引用替换为直接引用
类的初始化
- 在该阶段,主要就是对类变量进行初始化
类的初始化步骤
- 假如类还未被加载和连接,则程序先加载并连接该类
- 加入该类的直接父类还未被初始化,则先初始化其直接父类
- 加入类中有初始化语句,则系统依次执行这些初始化语句
注意:在执行第二个步骤的时候,系统对直接父类的初始化步骤也遵循初始化也遵循初始化步骤1-3
类的初始化时机:
- 创建类的实例
- 调用类的类方法
- 访问类或者接口的类变量,或者为该类变量赋值
- 使用反射方式来强制创建某个类或接口对应的java.lang.Class对象
- 初始化某个类的子类
- 直接使用java.exe命令来运行某个主类
类加载器
类加载器的作用
- 负责将.class文件加载到内存中,并为之生成对应的java.lang.Class对象
- 虽然我们不用过分关心类加载机制,但是了解这个机制我们就能更好的理解程序的运行
JVM的类加载机制
- 全盘负责:就是当一个类加载器负责加载某个Class时,该Class所依赖的和引用的其他Class也将由该类加载器负责载入,除非显示使用另外一个类加载器来载入
- 父类委托:就是当一个类加载器负责加载Class时,先让父类加载器试图加载该Class,只有在父类加载器无法加载该类时才尝试从自己的类路径中加载该类
- 缓存机制:保证所有加载过的Class都会被缓存,当程序需要使用某个Class对象时,类加载器先从缓存区中搜索该Class,只有当缓存区中不存在该Class对象时,系统才会读取该类对应的二进制数据,并将其转换成Class对象,存储到缓存区
- 类加载器的继承关系:System的父加载器为Platform,而Platform的父加载器为Bootstrap
ClassLoader中的两个方法
- static ClassLoader getSystemClassLoad():返回用于委派的系统类加载器
- ClassLoader getParent():返回父类加载器进行委派
反射
概述:
Java反射机制:是指在运行时去获取一个类的变量和方法信息。然后通过获取到的信息来创建对象,调用方法的一种机制。由于这种动态性,可以极大的增强程序的灵活性,程序不用再编译期就完成确定,在运行期仍然可以扩展
获取Class类的对象
我们要想通过反射区使用一个类,首先我们要获取到该类的字节码文件对象,也就是类型为Class类型的对象
这里我们提供三种方式获取Class类型的对象
实用类的Class属性来获取该类对应的Class对象。举例:Student.Class将会返回Student类对应的Class对象
调用对象的getClass()方法,返回该对象所属类对应的Class对象
该方法是Object类中的方法,所有的Java对象都可以调用该方法
使用Class类中的静态方法forName(String className),该方法需要传入字符串参数,该字符串参数的值是某个类的全路径,也就是完整包名的路径
三种方法使用示例:
反射获取构造方法并使用
获取示例:
反射获取构造方法使用练习1
- 基本数据类型也可以通过.class得到对应的Class类型
反射获取构造方法使用练习2
- public void setAccessible(boolean flag):值为true,取消访问检查
称为暴力反射
新建对象的时候无法使用私有的构造方法 而反射可以
反射获取成员变量并使用
示例:
反射获取成员变量并使用练习
反射获取成员方法
示例:
使用练习
反射练习之越过泛型检查
反射练习之运行配置文件指定内容
模块化
概述:
模块的基本使用
创建module-info.java描述文件 exports 包 导出包 requires 模块 依赖模块
模块服务的使用
服务:从Java6开始,Java提供了一种服务机制,允许服务提供者和服务使用者之间完成解耦。简单地说,就是服务使用者只面向接口编程,但不清楚服务提供者的实现类
uese 声明服务接口 provides 为服务接口指定实现类
模块服务的使用步骤
使用的话还需要 加载服务
调用load方法 参数为.Class 可以遍历
🎇🎇🎇