- JAVA基础入门
- Java语言概述
- 1.java概述
- 2.常用的DOS命令
- 3.JDK和JRE
- 4.编写HelloWorld程序
- 4.1编写步骤(初学者)
- 4.2编译、运行
- 基础语法
- 1.注释
- 2.关键字
- 3.数据类型
- 4.常量
- 5.变量
- 6.标识符
- 7.类型转换
- 7.1自动类型转换
- 7.2强制类型转换
- 运算符
- 1.算数运算符
- 2.赋值运算符
- 3.自增自减运算符
- 4.关系运算符
- 5.逻辑运算符
- 6.三元运算符
- 7.运算优先级
- 循环语句
- 1.for循环
- 2.while循环
- 3.do...while循环
- 4.区别
- 5.死循环
- 6.嵌套循环
- 7.跳转控制语句
- 7.1break语句
- 7.2continue语句
- 分支语句
- 1.if语句
- 2.if语句和三元运算符的互换
- 3.Switch语句
- Random
- 键盘输入数据
- 数组
- 1.格式
- 2.数组元素访问
- 3.动态初始化
- 4.静态初始化
- 5.内存分配
- 6.数组操作两个常见异常
- 7.数组常见操作
- 7.1遍历数组
- 7.2获取最值
- 方法
- 1.方法概述
- 2.方法定义和调用
- 2.1定义格式:
- 2.2调用格式:
- 3.带参数方法的定义和调用
- 3.1带参数定义格式:
- 3.2带参数调用格式:
- 4.带返回值方法的定义和调用
- 4.1带返回值定义格式:
- 4.2带返回值调用格式:
- 5.注意事项
- 5.1方法注意
- 5.2通用格式
- 6.方法重载
- 6.1概述
- 6.2特点
- 7.方法的参数传递
- Debug
- 面向对象基础
- 1.类和对象
- 1.1类的特点:
- 1.2类和对象的关系:
- 1.3类的组成:
- 1.4对象的使用
- 2.成员变量和局部变量
- 3.封装
- 3.1private关键字
- 3.2 private关键字的使用
- 3.3this关键字
- 3.4封装好处
- 4.构造方法
- 4.1概述
- 4.2注意事项
- 4.3标准类制作
- 字符串
- 1.API(应用程序编程接口)
- 2.String
- 2.1String概述
- 2.2构造方法:
- 2.3特点:
- 2.4字符串比较
- 3.StringBuilder
- 3.1构造方法:
- 3.2StringBuilder的添加和反转方法:
- 3.3StringBuilder和String相互转换:
- 3.4字符串拼接:
- 集合基础
- 1.集合
- 2.构造方法
- 3.常用方法
- 修饰符
- 1.包
- 2.导包
- 3.修饰符
- 4.状态修饰符
- 继承
- 1.继承的概念
- 2.格式
- 3.特点:
- 4.什么时候用继承?
- 5.成员变量的访问特点:
- 6.构造方法的访问特点:
- 7.成员方法的访问特点:
- 8.方法重写
- 9.Java中继承注意:
- 多态
- 1.概念
- 2.多态的前提和体现:
- 3.成员访问特点
- 4.利弊
- 5.多态中的转型
- 抽象类
- 1.抽象类描述
- 2.抽象类的特点
- 3.抽象类的成员特点
- 接口
- 1.接口概述
- 2.接口的特点
- 3.接口的成员特点
- 4.类和接口的关系
- 5.抽象类和接口的区别
- 形参和返回值
- 1.类名作为形参和返回值
- 2.抽象类名作为形参和返回值
- 3.接口名作为形参和返回值
- 内部类
- 1.内部类概述
- 2.成员内部类
- 3.局部内部类
- 4.匿名内部类
- 常用API
- 1.math
- 2.System
- 3.Object
- 4.Arrays
- 5.基本类型包装类
- 5.1常用的操作之一:
- 5.2 Integer类:
- 5.3 int和String的相互转换
- 5.4自动装箱和拆箱
- 6.日期类
- 6.1 Date类的构造方法:
- 6.2 Date类的常用方法:
- 6.3 SimpleDateFormat
- 6.4 SimpleDateFormat的构造方法:
- 6.5 SimpleDateFormat格式化和解析日期
- 6.6 Calendar类
- 6.7 Calendar的常用方法
- 异常
- 1.异常
- 2.JVM的默认处理方案
- 3.异常处理
- 4.异常处理之try ... catch ...
- 5.Throwable的成员方法
- 6.编译时异常和运行时异常的区别
- 7.异常处理之throws
- 8.自定义异常
- 9.throws和throw的区别
- 集合进阶
- 1.Collection
- 1.1 集合类的特点
- 1.2 Collection集合概述
- 1.3 创建Collection集合的对象
- 1.4 Collection集合常用方法
- 1.5 Collection集合的遍历
- 2.List
- 2.1 List集合概述
- 2.2 List集合特有方法:
- 2.3 并发修改异常
- 2.4 Listlterator
- 2.5 增强for循环
- 2.6 数据结构
- 2.7 常见的数据结构之栈
- 2.8 常见的数据结构之队列
- 2.9 常见的数据结构之数组
- 2.10 常见的数据结构之链表
- 2.11 List集合子类特点
- 2.12 linkedList集合的特有功能
- 3.Set
- 3.1 Set集合概述和特点
- 3.2 哈希值
- 3.3 HashSet集合概述和特点
- 3.4 HashSet集合保证元素唯一性源码分析
- 3.5常见数据结构之哈希表
- 3.6 linkedHashSet集合概述和特点
- 3.7 TreeSet集合概述和特点
- 3.8自然排序Comparable的使用
- 3.9比较器排序Comparator的使用
- 4.泛型
- 4.1泛型概述
- 4.2泛型类
- 4.3泛型方法
- 4.4泛型接口
- 4.5类型通配符
- 4.6可变参数
- 4.7可变参数的使用
- 5.Map
- 5.1 Map集合概述和使用
- 5.2 Map集合的基本功能
- 5.3 Map集合的获取功能
- 5.4 Map集合的遍历
- 6.Collections
- 6.1 Collections概述和使用
- IO流
- 1.file
- 1.1 File类概述和构造方法
- 1.2 file类创建功能
- 1.3 file类判断和获取功能
- 1.4 file类的删除功能
- 1.5 递归
- 2.字节流
- 2.1 IO流概述和分类
- 2.2字节流写数据
- 2.3字节流写数据的3种方式
- 2.4字节流写数据的两个小问题
- 2.5字节流写数据加异常处理
- 2.6字节流读数据(一次读一个字节数据)
- 2.7字节缓冲流
- 3.字符流
- 3.1为什么会出现字符流
- 3.2编码表
- 3.3字符串中的编码解码问题
- 3.4字符流中的编码解码问题
- 3.5字符流写数据的5种方法
- 3.6字符流读数据的2种方法
- 3.7字符缓冲流
- 3.8字符缓冲流特有功能
- 3.9复制文件异常处理
- 4.特殊操作流
- 4.1标准输入输出流
- 4.2打印流
- 4.3对象序列化流
- 4.4 Properties
- 多线程
- 1.实现多线程
- 1.1进程
- 1.2线程
- 1.3多线程的实现方法
- 1.4设置和获取线程名称
- 1.5线程调度
- 1.6线程控制
- 1.7线程生命周期
- 1.8多线程的实现方式
- 2.线程同步
- 2.1同步代码块
- 2.2同步方法
- 2.3线程安全的类
- 2.4 Lock锁
- 3.生产者消费者
- 3.1生产者消费者模式概述
- 3.2生产者消费者案例
- 网络编程
- 1.网络编程入门
- 1.1网络编程概述
- 1.2网络编程三要素
- 1.3IP地址
- 1.4InetAddress的使用
- 1.5端口
- 1.6协议
- 2.UDP通信程序
- 2.1UDP通信原理
- 2.2UDP发送数据
- 2.3UDP接受数据
- 2.4UDP通信程序联系
- 3.TCP通信程序
- 3.1通信原理
- 3.2TCP发送数据
- 3.3TCP接受数据
- 3.4TCP通讯程序练习
- Lambda表达式
- 1.Lambda表达式
- 1.1Lambda表达式的标准格式
- 1.2Lambda表达式练习
- 1.3Lambda表达式省略模式
- 1.4Lambda表达式的注意事项
- 1.5Lambda表达式和匿名内部类的区别
- 接口组成更新
- 1.接口组成更新
- 1.1接口组成更新概述
- 1.2接口中的默认方法
- 1.3接口中的静态方法
- 1.4接口中的私有方法
- 方法引用
- 1.方法引用
- 1.1方法引用符
- 1.2Lambda表达式支持的方法引用
- 1.3引用类方法
- 1.4引用对象的实例方法
- 1.5引用类的实例方法
- 1.6引用构造器
- 函数式接口
- 1.函数式接口
- 1.1函数式接口概述
- 1.2函数式接口作为方法的参数
- 1.3函数式接口作为方法的返回值
- 1.4常用的函数式接口
- 1.5Supplier接口
- 1.6Consumer接口
- 1.7Predicate接口
- 1.8Function接口
- Stream流
- 1.Stream流
- 1.1体验Stream流
- 1.2 Stream流的生成方式
- 1.3 Stream流的常见中间操作方法
- 1.4 Stream流的常见终结操作方法
- 1.5 Stream流的练习
- 1.6 Stream流的收集操作
- 反射
- 1.类加载器
- 1.1类加载
- 1.2类加载器
- 2.反射
- 2.1反射概述
- 2.2获取Class类的对象
- 2.3反射获取构造方法并使用
- 2.4反射获取构造方法并使用练习
- 2.5反射获取成员变量并使用
- 2.6反射获取成员变量并使用练习
- 2.7反射获取成员方法并使用
- 2.8反射获取成员方法并使用练习
- 2.9反射练习
- 模块化
- 1.模块化
- 1.1模块的基本使用
- 1.2模块服务的使用
Java是sun公司开发的一门编程语言,目前被Oracle公司收购,编程语言就是用来编写软件的。
Java语言平台
- JavaSE(标准版)部分,基础班学习JavaSE,JavaSE并不能开发大型项目。
- JavaEE(企业版)部分,就业班学习JavaEE,学习完JavaEE部分就可以开发各种大型项目了。
cd… : 退回到上一级目录
cd : 退回到根目录
cd tools: 进入tools文件夹
d: : 回车 盘符切换
cd d:234 :进入d盘的234文件夹,再切换盘符(d:)才能进入d:234
dir : 列出当前目录下的文件以及文件夹
cls : 清除屏幕
ipconfig: 查看本机的相关网络配置
3.JDK和JREJDK与JRE的关系
a: JDK:它是Java开发运行环境,在程序员的电脑上当然要安装JDK;
b: JRE:Java Runtime Environment它是Java运行环境;
c: JDK包含了JRE。
实现跨平台需要依赖Java的虚拟机 JVM (Java Virtual Machine)
4.编写HelloWorld程序 4.1编写步骤(初学者)a: 创建一个普通文本文件,将其修改为.java文件。
b: 完成模板代码:
public class HelloWorld{
public static void main(String[] args) {
System.out.println(“Hello World!”);
}
}
c:保存
4.2编译、运行开启DOS窗口并切换到.java文件所在的目录 ,输入指令
编译:javac 空格 文件名.java //生成一个CLASS文件
javac helloworld.java
执行:java 空格 类名 //类名为CLASS 文件名称,不带后缀
java helloworld
基础语法 1.注释1:单行注释 //注释内容
2:多行注释 /注释内容/
3:文档注释 /*注释内容/
组成关键字的字母全部小写,有特殊颜色标记 ,注意String不是关键字
3.数据类型1B(字节)=8bit
- 基本数据类型:整数(byte 1B、short 2B、int 4B、long 8B)、小数(float 4B、double 8B)、字符类型(char 2B)、布尔类型(boolean 1B)
- 引用数据类型: 数组([])、类(class)、接口(interface)
常量就是不变的数据量, 在程序执行的过程中其值不可以发生改变
常量分类
a: 整数类型
十进制表示方式:正常数字 如 13、25等
二进制表示方式:以0b(0B)开头 如0b1011 、0B1001
十六进制表示方式:以0x(0X)开头 数字以0-9及A-F组成 如0x23A2、0xa、0x10
八进制表示方式:以0开头 如01、07、0721
b: 小数类型
如1.0、-3.15、3.168等
c: 布尔类型
true、false
d: 字符类型
如’a’,‘A’, ‘0’, ‘家’
字符必须使用’’ 包裹,并且其中只能且仅能包含一个字符。
e: 字符串类型
字符串String类型是一种引用类型,我们先了解作为常量类型的使用方式
如“我爱Java”,“0123”,“null”
字符串必须使用“”包裹,其中可以包含0~N个字符。
程序运行过程中,值可以改变
变量定义格式:数据类型 变量名= 变量值;
修改值格式:变量名=修改值;
注意:
a:名字不能重复
b:变量未赋值,不能使用(尚未初始化变量)
c:定义long类型变量时,为了防止整数过大,后面要+“L”
d:定义float类型变量时,为了防止类型不兼容,后面要+“F”
6.标识符 给类,接口,方法,变量等起名字时使用的字符序列
规则:由数字、字母、下划线(_)和美元符($)组成
注意:不能以数字开头,不能是关键字,区分大小写
标识符中常见的命名规则:
- a: 包名:多单词组成时所有字母均小写,使用.连接 aaa.bbb.ccc
- b: 类名&接口名:大驼峰式 AaaBbbCcc
- c: 变量名&方法名:小驼峰式 aaaBbbCcc
- d: 常量名:多单词组成是所有字母均大写,使用_连接AAA_BBB_CCC
表示范围小的数据类型转换成范围大的数据类型
范围从小到大:byte–>short(char)–>int–>long–>float–>double
格式:范围大的数据类型 变量 = 范围小的数据类型值;
double d=100;//把100转换成double型
7.2强制类型转换表示范围大的数据类型转换成范围小的数据类型
格式:范围小的数据类型 变量 = (范围小的数据类型) 范围大的数据类型值;
int i = (int)6.718; //i的值为6
一般不建议,因为有数据丢失
运算符 1.算数运算符+、-、*、/、%(取余)
a:加法运算符在连接字符串时要注意,只有直接与字符串相加才会转成字符串。
b:除法“/”当两边为整数时,取整数部分,舍余数。当其中一边为浮点型时,按正常规则相除。
c:“%”为整除取余符号,小数取余没有意义。结果符号与被取余符号相同。
d:整数做被除数,0不能做除数,否则报错。
e:小数做被除数,整除0结果为Infinity,对0取模结果为NaN。
f:算数表达式中包含多个基本数据类型的值时,整个表达式类型提升到其中最高等级类型。
2.赋值运算符| 运算符 | 运算规则 | 范例 | 结果 |
|---|---|---|---|
| = | 赋值 | int a=2 | 2 |
| += | 加后赋值 | int a=2,a+=2 | 4 |
| -= | 减后赋值 | int a=2,a-=2 | 0 |
| *= | 乘后赋值 | int a=2,a*=2 | 4 |
| /= | 整除后赋值 | int a=2,a/=2 | 1 |
| %= | 取余后赋值 | int a=2,a%=2 | 0 |
++、–
单独使用时:i++和++i结果是一样的
参与操作时:int j=i++;先把i值赋给j,后i做++操作。
int j=i++;先i做++操作,后把i值赋给j。
4.关系运算符| == | != | > | >= | < | <= |
|---|---|---|---|---|---|
| 等于 | 不等于 | 大于 | 大于等于 | 小于 | 小于等于 |
| 运算符 | 名称 | 运算规则 |
|---|---|---|
| & | 逻辑与 | 都是true结果才为true |
| | | 逻辑或 | 有true结果就为true |
| ^ | 逻辑异或 | 结果不同为true,相同为false |
| ! | 逻辑非 | 结果相反 |
| && | 短路与 | 左边为真,右边执行;左边为假,右边不执行 |
| || | 短路或 | 左边为假,右边执行;左边为真,右边不执行 |
(条件表达式)?表达式1:表达式2;
判断条件表达式,结果为一个布尔值,
true,运算结果为表达式1,
false,运算结果为表达式2。
| 优先级 | 描述 | 运算符 |
|---|---|---|
| 1 | 括号 | ()、[] |
| 2 | 正负号 | +、- |
| 3 | 自增自减,非 | ++、–、! |
| 4 | 乘除,取余 | *、/、% |
| 5 | 加减 | +、- |
| 6 | 移位运算 | <<、>>、>>> |
| 7 | 大小关系 | >、>=、<、<= |
| 8 | 相等关系 | ==、!= |
| 9 | 按位与 | & |
| 10 | 按位异或 | ^ |
| 11 | 按位或 | | |
| 12 | 逻辑与 | && |
| 13 | 逻辑或 | || |
| 14 | 条件运算 | ?: |
| 15 | 赋值运算 | =、+=、-=、*=、/=、%= |
| 16 | 位赋值运算 | &=、=、<<=、>>=、>>>= |
格式:
for(初始化语句 ; 条件判断语句 ; 条件控制语句){
循环体;
}
2.while循环
格式:
初始化表达式;
while(条件判断语句){
循环体;
条件控制语句
}
3.do…while循环
格式:
初始化表达式;
do{
循环体;
条件控制语句
}while(条件判断语句);
4.区别
-
for循环和while循环是先判断后执行
do…while循环是先执行后判断
-
for循环结束后,初始化语句定义的自增变量归属for循环语法结构,不能再被访问
while循环结束后,初始化语句定义的自增变量不归属while循环语法结构,可以继续使用
无限循环存在的原因是并不知道循环多少次,而是根据某些条件,来控制循环
- for( ; ; ){ }
- while(true){ } (常用)
- do{ }while(true){ };
Ctrl+c可以结束死循环
6.嵌套循环格式:
for(初始化表达式; 循环条件; 操作表达式) {
………
for(初始化表达式; 循环条件; 操作表达式) {
执行语句
………
}
………
}
//打印正三角形
public class ForForDemo {
public static void main(String[] args){
for(int i = 0 ; i < 9 ; i++){//外循环,控制的是行数
for(int j = 0; j < i+1 ;j++){//内循环,控制的是每行的个数 System.out.print("* ");
}
System.out.println();
}
}
}
7.跳转控制语句
7.1break语句
跳出所在的循环体
- 无法单独使用,必须将break关键字置于switch或循环语句中
- 只能跳出最近的代码块,不能跨越多级代码块
- 如果用在循环中,基于条件控制,终止循环体内容的执行,结束当前整个循环
提前结束本次循环,继续进行下次循环
- 无法单独使用,必须将continue关键字置于循环语句中
- 如果用在循环中,基于条件控制,跳过某次循环体内容的执行,继续下一次的执行
格式1:
if(比较表达式) {
语句体;
}
执行流程:
先计算比较表达式的值,看其返回值是true还是false。
如果是true,就执行语句体;
如果是false,就不执行语句体。
格式2:
if(比较表达式) {
语句体1;
}else {
语句体2;
}
执行流程:
首先计算比较表达式的值,看其返回值是true还是false。
如果是true,就执行语句体1;
如果是false,就执行语句体2。
格式3:
if(比较表达式1) {
语句体1;
}else if(比较表达式2) {
语句体2;
}else if(比较表达式3) {
语句体3;
}
...
else {
语句体n+1;
}
执行流程:
首先计算比较表达式1看其返回值是true还是false,
如果是true,就执行语句体1,if语句结束。
如果是false,接着计算比较表达式2看其返回值是true还是false。
格式: 条件表达式?表达式1:表达式2;
执行流程:
判断条件表达式,结果为一个布尔值,
true,运算结果为表达式1,
false,运算结果为表达式2。
格式:
swtich(表达式){
case 常量1 :
语句体1;
break;
case 常量2 :
语句体2;
break;
case 常量3 :
语句体3;
break;
...
default:
语句体n+1;
break;//可省略
}
case穿透:在使用switch语句的过程中,如果多个case条件后面的执行语句是一样的,则该执行语句只需书写一次即可,这是一种简写的方式。
例如,要判断一周中的某一天是否为工作日,同样使用数字1~7来表示星期一到星期天,当输入的数字为1、2、3、4、5时就视为工作日,否则就视为休息日。
import java.util.Scanner;
public class SwitchDemo {
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
int week = sc.nextInt();
switch (week) {
case 1:
case 2:
case 3:
case 4:
case 5:
System.out.println("工作日");
break;
case 6:
case 7:
System.out.println("休息日");
break;
default:
System.out.println("输入的数字不正确...");
break;
}
}
}
Random
用于产生随机数
1.导包:import java.util.Random;
2.创建对象:Random r=new Random();
3.获取随机数:int number=r.nextInt(10);//表示从[0,10)中获取随机数
int number=r.nextInt(10)+1;//表示从[1,11)中获取随机数
int number=r.nextDouble();// 产生[0,1)范围的随机数
键盘输入数据1.导包:import java.util.Scanner;
2.创建对象:Scanner sc=new Scanner(System.in);
3.接收数据:int x = sc.nextInt();
数组 1.格式数据类型[] 数组名;
int[] arr;
2.数组元素访问访问数组存储的元素,必须依赖于索引:数组名[索引]
3.动态初始化动态初始化 : 在定义数组时只指定数组的长度,由系统自动为元素赋初值的方式称作动态初始化。
格式:类型[] 数组名 = new 类型[长度];
int[] arr = new int[4];
new为数据申请内存空间
4.静态初始化静态初始化: 在初始化数组时还有一种方式叫做静态初始化,就是在定义数组的同时就为数组的每个元素赋值。
格式:类型[] 数组名 = new 类型[]{元素,元素,……};
int[] arr = new int[]{1,2,3,4};
简化格式:类型[] 数组名 = {元素,元素,元素,……};
int[] arr = { 1, 2, 3, 4 };
5.内存分配int[] arr = new int[4];
int[] arr:栈内存,使用完毕立即消失
new int[4]:堆内存,使用完毕,会在垃圾回收器空闲时间被回收
数组在初始化时,会为存储空间添加默认值。
6.数组操作两个常见异常-
数组的索引越界异常:ArrayIndexOutOfBoundsException
访问了数组中不存在的索引元素
-
空指针异常:NullPointerException
访问的数组已经不在指向堆内存的数据
int[] arr = {1,5,8};
System.out.println(arr[2]);
arr = null; // arr 不在保存数组的地址了
System.out.println(arr[2]);//报错
public class ArrayDemo1 {
public static void main(String[] args) {
int[] arr = { 1, 2, 3, 4, 5 }; // 定义数组
// 使用for循环遍历数组的元素
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]); // 通过索引访问元素
}
}
}
7.2获取最值
public class ArrayDemo2 {
public static void main(String[] args) {
int[] arr = { 4, 1, 6, 3, 9, 8 }; // 定义一个数组
int max = arr[0]; // 定义变量max用于记住最大数,首先假设第一个元素为最大值
// 下面通过一个for循环遍历数组中的元素
for (int x = 1; x < arr.length; x++) {
if (arr[x] > max) { // 比较 arr[x]的值是否大于max
max = arr[x]; // 条件成立,将arr[x]的值赋给max
}
}
System.out.println("max=" + max); // 打印最大值
}
}
方法
1.方法概述
方法(method):是将具有独立功能的代码块组织成为一个整体,使其具有特殊功能的代码集
注意:
方法定义:方法必须先创建才可以使用
方法调用:方法创建后并不是直接运行的,需要手动使用后才执行
2.方法定义和调用 2.1定义格式:public static void 方法名(){
//方法体
}
方法名();
在main方法中调用
必须先定义后调用,否则程序报错
3.带参数方法的定义和调用 3.1带参数定义格式:public static void 方法名(参数){ ... }
参数为形参:数据类型+变量名 , 缺一不可,可多个参数,用逗号(,)隔开
3.2带参数调用格式:方法名(参数);
参数为实参:变量名/常量值,数量与类型必须与定义中一致
4.带返回值方法的定义和调用 4.1带返回值定义格式:public static 数据类型 方法名(参数){
return 数据;
}
返回数据与方法定义数据类型相匹配。
4.2带返回值调用格式:格式1:方法名(参数);
格式2:数据类型 变量名=方法名(参数);(变量接受)
方法的返回值通常会使用变量接收,否则该返回值将无意义
5.注意事项 5.1方法注意- 方法不能嵌套定义
- void表示无返回值,可以省略retum,也可以单独的书写return,后面不加数据
定义:
public static 返回值类型 方法名(参数){
方法体;
return 数据;
}
调用:数据类型 变量名=方法名(参数);
void类型的方法,直接调用即可
非void类型的方法,推荐用变量接收调用
方法重载指同一个类中定义的多个方法之间的关系,满足下列条件的多个方法相互构成重载
- 多个方法在同一个类中
- 多个方法具有相同的方法名
- 多个方法的参数不相同,类型不同或者数量不同
- 重载仅对应方法的定义,与方法的调用无关。
- 重载仅针对同一个类中方法的名称与参数进行识别,与返回值无关,换句话说不能通过返回值来判定两个方法是否相互构成重载
基本类型:对于基本数据类型的参数,形式参数的改变,不影响实际参数的值
引用类型:对于引用类型的参数,形式参数的改变,影响实际参数的值
DebugDebug:查看程序的执行流程,调试程序(断点调试)
Debug使用注意:如果数据来自于键盘输入,一定要输入数据,不然就不能继续往下查看了
面向对象基础 1.类和对象类是对现实生活中一类具有共同属性和行为的事物的抽象。
1.1类的特点:- 类是对象的数据类型
- 类是具有相同属性和行为的一组对象的集合
- 类:类是对现实生活中一类具有共同属性和行为的事物的抽象
- 对象:是能够看得到摸的着的真实存在的实体
属性和行为
- 属性:在类中通过成员变量来体现(类中方法外的变量)
- 行为:在类中通过成员方法来体现(和前面的方法相比去掉static关键字即可)
创建对象:类名 对象名= new 类名();
使用对象:
- 使用成员变量:对象名.变量名
- 使用成员方法:对象名.方法名()
| 区别 | 成员变量 | 局部变量 |
|---|---|---|
| 类中位置不同 | 类中方法外的变量 | 方法中的变量 |
| 内存中位置不同 | 在堆内存 | 在栈内存 |
| 生命周期不同 | 随着对象的存在而存在,随着对象的消失而消失 | 随着方法的调用而存在,随着方法的调用完毕而消失 |
| 初始化值不同 | 有默认的初始化值 | 没有默认初始化值,必须先定义赋值才能使用 |
- 是一个权限修饰符
- 可以修饰成员(成员变量和成员方法)
- 作用是保护成员不被别的类使用,被private修饰的成员只在本类中才能访问
针对private修饰的成员变量,如果需要被其他类使用,提供相应的操作
- 提供“get变量名()”方法,用于获取成员变量的值。方法用public修饰
- 提供“set变量名(参数)”方法,用于设置成员变量的值,方法用public修饰
一个标准类的编写:
- 把成员变量用private修饰
- 提供对应的getXxx()/setXxx()方法
this修饰的变量用于指代成员变量
- 方法的形参如果与成员变量同名,不带this修饰的变量指的是形参,而不是成员变量
- 方法的形参没有与成员变量同名,不带this修饰的变量指的是成员变量
什么时候使用this呢?解决局部变量隐藏成员变量
this:代表所在类的对象引用
- 记住:方法被哪个对象调用,this就代表哪个对象
通过方法来控制成员变量的操作,提高了代码的安全性
把代码用方法进行封装,提高了代码的复用性
4.构造方法 4.1概述构造方法是一种特殊的方法
作用:创建对象
格式:
public class类名{
修饰符类名(参数){}
}
功能:主要是完成对象数据的初始化
4.2注意事项构造方法的创建
- 如果没有定义构造方法,系统将给出一个默认的无参数构造方法
- 如果定义了构造方法,系统将不再提供默认的构造方法
构造方法的重载
- 如果自定义了带参构造方法,还要使用无参数构造方法,就必须再写一个无参数构造方法
推荐的使用方式
- 无论是否使用,都手工书写无参数构造方法
成员变量
- 使用private修饰
构造方法
- 提供一个无参构造方法
- 提供一个带多个参数的构造方法
成员方法
- 提供每一个成员变呈对应的setXxx()/getXxx()
- 提供一个显示对象信息的show()
创建对象并为其成员变量赋值的两种方式
- 无参构造方法创建对象后使用setXxxx(赋值)
- 使用带参构造方法直接创建带有属性值的对象
nextLine()读取结果为String类型,返回String类型。
nextLine()读取回车符,并只以回车符结束。
next()、nextInt()看到空格符和回车符都认为读取结束。
2.String 2.1String概述String类在java.long包下,所以使用时不需要导包
字符串不可变,但可共享
效果上相当于char[],但底层原理是byte[]。
2.2构造方法:| 方法名 | 说明 |
|---|---|
| public String() | 创建一个空白字符串对象,不含有任何内容 |
| public String(char[] chs) | 根据字符数组的内容,来创建字符串对象 |
| public String(byte[] bys) | 根据字节数组的内容,来创建字符串对象 |
| String s = “abc” ; | 直接赋值的方式创建字符串对象,内容就是abc |
- 通过new创建的字符串对象,每一次new都会申请一个内存空间,虽然内容相同,但是地址值不同
- 以" ”方式给出的字符串,只要字符序列相同(顺序和大小写),无论在程序代码中出现几次,JVM都只会建立一个String 对象,并在字符串池中维护
使用==作比较
- 基本类型:比较的是数据值是否相同
- 引用类型:比较的是地址值是否相同
equels()方法用来比较内容是否相同
charAt()对应的是索引对应值
endsWith()是否以此后缀结尾
补充创建对象和调用方法:Ctrl+Alt+V
3.StringBuilderStringBuilder对象中内容可变
3.1构造方法:| 方法名 | 说明 |
|---|---|
| public StringBuilder() | 创建—个空白可变字符串对象,不含有任何内容 |
| public StringBuilder(String str) | 根据字符串的内容,来创建可变字符串对象 |
| 方法名 | 说明 |
|---|---|
| public StringBuilder append(任意类型) | 添加数据,并返回对象本身 |
| public StringBuilder reverse() | 返回相反的字符序列 |
- StringBuilder转换为String
public String toString():通过toString(就可以实现把StringBuilder转换为String - String转换为StringBuilder
public StringBuilder(String s):通过构造方法就可以实现把String转换为StringBuilder
链式编程:sb.append("hello").append("world").append("java").append("100");
集合基础 1.集合ArrayList:可调整数组大小的数组实现。
:是一种特殊的数据类型,泛型。
2.构造方法| 方法名 | 说明 |
|---|---|
| public ArrayList() | 创建一个空的集合对象 |
| public boolean add(E e) | 将指定的元素追加到此集合的末尾 |
| public void add(int index,E element) | 在此集合中的指定位苦插入指定的元素 |
| 方法名 | 说明 |
|---|---|
| public boolean remove(Object o) | 删除指定的元素,返回删除是否成功 |
| public E remove(int index) | 删除指定索引处的元素,返回被删除的元素 |
| public E set(int index,Eelement) | 修改指定索引处的元素,返回被修改的元素 |
| public E get(int index) | 返回指定索引处的元素 |
| public int size() | 返回集合中的元素的个数 |
就是文件夹,用来分类管理
格式:package 包名(多级包用.分开)
自动建包:javac -d. HelloWorld.java
2.导包格式:import 包名;
自动导包:Alt+Enter
3.修饰符| 同一个类中 | 同一个包中子类无关 | 不同包子类 | 不同包无关类 | |
|---|---|---|---|---|
| private | √ | |||
| 默认 | √ | √ | ||
| protected | √ | √ | √ | |
| public | √ | √ | √ | √ |
final(最终):
- 修饰方法,方法不能被重写
- 修饰变量,变量是常量,不能再赋值
- 修饰类,不能被继承
final修饰局部变量:
-
变量是基本类型:final修饰的是基本类型的数据值不能变
-
变量是引用类型:final修饰的是引用类型的地址值不能变,但地址里面的内容可以变
final Student s = new Student();
s.age=10; 可以变
s=new Student(); 报错,不能变
static(静态):变量、方法
-
被类的所有对象共享
-
可以通过类名/对象名调用,推荐类名
静态成员方法只能访问静态成员。
继承是面向对象三大特征之一。可以使子类具有父类的属性和方法,还可以在子类中重新定义,追加属性和方法。
2.格式class 子类 extends 父类 {}
(派生类)(基类、超类)
-
子类可以有父类的内容
-
子类还可以有自己特有的内容
好处:
- 提高了代码的复用性(各个类相同的成员可以放到同一个类中)
- 提高了代码的维护性. (如果方法代码需要修改,修改一处即可)
弊端:
- 继承让类与类之间产生了关系, 类的耦合性增强了,当父类发生了变化时子类实现也不得不跟着变化,削弱了子类的独立性
- 满足A是B的一种,则A是子类,B是父类
子类局部方法 ----子类成员 ----父类成员
age this.age super. age
本类对象引用 父类对象引用
- 子类中所有的构造方法默认都会访问父类中的无参构造方法
原因:每个子类构造方法的第一条语句默认都是: super()。 - 如果父类中没有无参构造方法,只有带参构造方法,该怎么办?
通出使用super关键字去显示的调用父类中的带参构造方法。
在父类中自己提供一个无参构造方法。
子类成员 ----父类成员
8.方法重写方法重写就是子类中出现了和父类中一模一样的方法声明。
当子类需要父类的功能,而功能主体子类由自己的特有内容时,可以重写父类中的方法,这样既沿袭了父类的功能,又定义了子类特有的内容。
@Override(注解):可以帮助检查重写方法的方法声明的正确性。
注意:
- 私有方法不能被重写(父类私有成员子类不能继承)
- 此类方法访问权限不能低于父类(public>默认>private)
- Java中类只支持单继承,支持多层继承
- 所有的类都直接或者间接的继承了 Object类,Object类称为祖宗类
同一个对象,在不同时刻表现出来的不同时态。
2.多态的前提和体现:- 有继承/实现关系;
- 有方法重写;
- 有父类引用指向子类对象。
-
成员变量:编译看左边,执行看左边
-
成员方法:编译看左边,执行看右边
因为成员方法有重写。
- 好处:提高程序的扩展性。定义方法的时候,使用父类型作为参数,将来在使用的时候,使用具体的子类型参与操作。
- 弊处:不能使用子类的特有功能。
向上转型:从子到父,父类引用指向子类对象。
Animal a = new Cat();
向下转型:从父到子,父类引用转为子类对象。
Cat c = (Cat)a;
抽象类 1.抽象类描述在Java中,一个没有方法体的方法应该定义为抽象方法,而类中如果有抽象方法,该类必须定义为抽象类
2.抽象类的特点- 抽象类和抽象方法必须使用abstract关键字修饰
public abstract class 类名{} public abstract void eat(); - 抽象类中不一定有抽象方法,有抽象方法的类一定是抽象类
- 抽象类不能实例化
参照多态的方式,通过子类对象实例化,这叫抽象类多态 - 抽象类的子类
要么重写抽象类中的所有方法
要么是抽象类
- 成员变量
可以是变量
也可以是常量 - 构造方法
有构造方法,但是不能实例化
作用是用于子类访问父类数据的初始化 - 成员方法
可以有抽象方法:限定子类必须完成某些动作
也可有非抽象方法:提高代码复用性
接口就是一种公共的规范标准,只要符合规范标准,大家都可以通用,Java中的接口更多的体现在对行为的抽象
2.接口的特点- 接口用关键字interface修饰
public interface 接口名{} - 类实现接口用implements表示
public class 类名 implements 接口名{} - 接口不能实例化
参照多态的方式,通过实现类对象实例化,这叫接口多态
多态的形式:具体类多态,抽象类多态,接口多态。
多态的前提:有继承或者实现关系;有方法重写;有父(类/接口)引用指向(子/实现)类对象 - 接口的实现类
要么重写接口中的所有抽象方法
要么是抽象类
- 成员变量
只能是常量
默认修饰符:public static final - 构造方法
接口没有构造方法,因为接口主要是对行为进行抽象的,是没有具体存在
一个类如果没有父类,默认继承Object类 - 成员方法
只能是抽象方法
默认修饰符:public abstract
- 类和类的关系
继承关系,只能单继承,但是可以多层继承 - 类和接口的关系
实现关系,可以单实现,也可以多实现,还可以在继承一个类的同时实现多个接口 - 接口和接口的关系
继承关系,可以单继承,也可以多继承
- 成员区别
抽象类 变量,常量;有构造方法;有抽象方法,也有非抽象方法
接口 常量;抽象方法 - 关系区别
类与类 继承,单继承
类与接口 实现,单实现,多实现
接口与接口 继承,单继承,多继承 - 设计理念区别
抽象类 对类抽象,包括属性、行为
接口 对行为抽象,主要是行为
- 方法的形参是类名,其实需要的是该类的对象
- 方法的返回值是类名,其实返回的是该类的对象
- 方法的形参是抽象类名,其实需要的是该抽象类的子类对象
- 方法的返回值抽象是类名,其实返回的是该抽象类的子类对象
- 方法的形参是接口名,其实需要的是该接口的实现类对象
- 方法的返回值是接口名,其实返回的是该接口的实现类对象
内部类:就是在一个类中定义一个类。在一个类A的内部定义一个类B,类B就被成为内部类
- 格式:public class 类名{ 修饰符 class 类名{ } }
内部类的访问特点
- 内部类可以直接访问外部类的成员,包括私有
- 外部类要访问内部类的成员,必须创建对象
按照内部类在类中定义的位置不同,可以分为如下两种形式
- 在类的成员位置:成员内部类
- 在类的局部位置:局部内部类
成员内部类,外界如何创建对戏想使用呢?
- 格式:外部类名.内部类名 对象名 = 外部类对象.内部类对象;
- 范例:Outer.Inter oi = new Outer().new Inter();
- 局部内部类是在方法中定义的类,所以外界是无法直接使用,需要在方法内部创建对象并使用
- 该类可以直接访问外部类的成员,也可以访问方法内的局部变量
前提:存在一个类或者接口,可以是具体类也可以是抽象类
- 格式:new 类名或者接口名(){ 重写方法; };
本质:是一个继承了该类或者实现了该接口的子类匿名对象
常用API 1.mathmath包含执行基本数字运算的方法
没有构造方法,想要使用类中成员,就看类的成员是否都是静态的,如果是,通过类名直接调用
math 类的常用方法:
| 方法名 | 说明 |
|---|---|
| public static int abs(int a) | 返回参数的绝对值 |
| public static double ceil(double a) | 返回大于或等于参数的最小double值,等于一个整数 |
| public static double floor(double a) | 返回小于或等于参数的最大double值,等于一个整数 |
| public static int round(float a) | 按照四舍五入返回最接近参数的int |
| public static int max(int a,int b) | 返回两个int值中的较大值 |
| public static int min(int a,int b) | 返回两个int值中的较小值 |
| public static double pow(double a,double b) | 返回a的b次幂的值 |
| public static double random( | 返回值为double的正值,[0.0,1.0) |
System包含几个有用的类字段和方法,它不能被实例化
System类的常用方法:
| 方法名 | 说明 |
|---|---|
| public static void exit(int status) | 终止当前运行的Java虚拟机,非零表示异常终止 |
| public static long currentTimeMillis() | 返回当前时间(以毫秒为单位) |
currentTimeMillis()方法结果 :在1970年1月1日UTC之间的当前时间和午夜之间的差异,以毫秒为单位。
3.ObjectObject是类层次结构的根。 每个类都有Object作为超类。 所有对象(包括数组)都实现了这个类的方法。
构造方法:public Object()
看方法的源码,选中方法,按下Ctrl+B
Object类的常用方法:
| 方法名 | 说明 |
|---|---|
| public String toString() | 返回对象的字符串表示形式。建议所有子类重写该方法,自动生成 |
| public boolean equals(Object obj) | 比较对象是否相等。默认比较地址,重写可以比较内容,自动生成 |
Arrays类包含用于操作数组的各种方法。
Arrays类的常用方法:
| 方法名 | 说明 |
|---|---|
| public static String toString(int[] a) | 返回指定数组的内容的字符串表示形式 |
| public static void sort(int[] a) | 按照数字顺序排列指定的数组 |
工具类的设计思想:
- 构造方法用private修饰,防止外界创建对象;
- 成员用public static修饰,可以使用类名来访问该成员方法。
冒泡排序:一种排序的方式,对要进行排序的数据中相邻的数据进行两两比较,将较大的数据放在后面,依次对所有的数据进行操作,直至所有数据按要求完成排序。
如果有n个数据进行排序,总共需要比较n-1次。
每一次比较完毕,下一次的比较就会少一个数据参与。
将基本数据类型封装成对象的好处在于可以在对象中定义更多的功能方法操作该数据。
5.1常用的操作之一:用于基本数据类型与字符串之间的转换
| 基本数据类型 | 包装类 |
|---|---|
| byte | Byte |
| short | Short |
| int | Integer |
| long | Long |
| float | Float |
| double | Double |
| char | Character |
| boolean | Boolean |
包装一个对象中的原始类型int的值。
| 方法名 | 说明 |
|---|---|
| public Integer(int value) | 根据int值创建Integer对象(过时) |
| public Integer(String s) | 根据String值创建Integer对象(过时) |
| public static Integer valueOf(int i) | 返回表示指定的int值的Integer实例 |
| public static Integer valueOf(String s) | 返回一个保存指定值的Integer对象String |
Integer类中的String值只能为数字组成的字符串。
5.3 int和String的相互转换 int转换为String: public static String valueOf(int i)
String转换为int: public static int parseInt(String s)
5.4自动装箱和拆箱 装箱:把基本数据类型转换为对应的包装类类型
Integer i = 100;//自动装箱
拆箱:把包装类类型装换为对应的基本数据类型
i += 200;//i自动拆箱,i=i+200赋值后自动装箱
只要是对象,在使用前就必须进行不为null的判断
Date代表了一个特定的时间,以毫秒的精度。
6.1 Date类的构造方法:| 方法名 | 说明 |
|---|---|
| public Date() | 分配一个Date对象,并初始化,以便它代表它被分配的时间,精确到毫秒 |
| public Date(long date) | 分配一个Date对象,并将其初始化为表示从标准基仙时间起指定的毫秒数 |
| 方法名 | 说明 |
|---|---|
| publie long getTime() | 获取的是日期对象从1970年1月1日00:00 00到现在的毫秒值 |
| public void setTime(long time) | 设置时间,给的是毫秒值 |
SimpleDateFormat是一个具体的类,用于以区域设置敏感的方式格式化和解析日期。重点学习日期格式化和解析
在日期和时间模式字符串中,从’A’到’Z’以及从’a’到’z’引号的字母被解释为表示日期或时间字符串的组件的模式字母。
常用的模式字母及对应关系如下:
| 年 | 月 | 日 | 时 | 分 | 秒 |
|---|---|---|---|---|---|
| y | M | d | H | m | s |
| 方法名 | 说明 |
|---|---|
| public SimpleDateFormat() | 构造一个SimpleDateFormat, 使用默认模式和日期格式 |
| public SimpleDateFormat(String pattern) | 构造一个SimpleDateFormat使用给定的模式和默认的日期格式 |
格式化(从Date到String):public final String format(Date date)
解析(从String到Date):public Date parse(String source)
6.6 Calendar类Calendar为某一时刻和一组日历字段之间的转换提供了一些方法,并为操作日历字段提供了一些方法。
Calendar提供了一个类方法getInstance用于获取Calendar对象,其日历字段已使用当前日期和时间初始化:
Calendar rightNow = Calendar.getInstance();//多态的形式
6.7 Calendar的常用方法| 方法名 | 说明 |
|---|---|
| public int get(int field) | 返回给定日历字段的值 |
| public abstract void add(int field, int amount) | 根据日历的规则,将指定的时间量添加或减去给定的日历字段 |
| public final void set(int year,int month,int date) | 设置当前日历的年月日 |
c.add(Calendar.YEAR,10);//10年
c.add(Calendar.DATE,-5);//5天前
月是0-11,要设置3月,得写2
异常就是程序出现了不正常的情况
异常体系:
Throwable(Java语言中所有错误和异常的超类)分为Error和Exception
- Error:严重问题,不需要处理
- Exception:称为异常类,它表示程序本身可以处理的问题
Exception分为RuntimeException和非RuntimeException
- RuntimeException:在编译期是不检查的,出现问题后需要我们回来修改代码
- 非RuntimeException:编译期就必须处理的,否则程序不能通过编译,就更不能正常运行了
如果程序出现问题,我们没有做任何处理,最终JVM会做默认的处理
- 把异常的名称、异常原因及异常出现的位置等信息输出在了控制台
- 程序停止执行
如果程序出现异常问题,我们需要自己来处理,有两种方案:
- try … catch …
- throws
格式:try { 可能出现异常的代码; } catch(异常类名 变量名) { 异常的处理代码; }
执行流程:程序从try里面的代码开始执行,出现异常,会自动生成一个异常类对 象,该异常对象将被提交给JAVA运行时系统,当JAVA运行时系接收到异 常对象时,会到catch中去找匹配的异常类,找到后进行异常的处理, 执行完毕之后,程序还可以继续往下执行。
5.Throwable的成员方法| 方法名 | 说明 |
|---|---|
| public String getMessage() | 返回此throwable的详细消息字符串 |
| public String toString() | 返回此可抛出的简短描述 |
| public void printStackTrace() | 把异常的错误信息输出在控制台 |
Java中的异常被分为两大类: 编译时异常和运行时异常,也被称为受检异常和非受检异常,所有的RuntimeException类及其子类被称为运行时异常,其他的异常都是编译时异常
- 编译时异常: 必须显示处理,否则程序就会发生错误,无法通过编译
- 运行时异常: 无需显示处理,也可以和编译时异常一样处理
虽然我们通过try…catch…可以对异常进行处理,但是并不是所有的情况我们都有权限进行异常的处理也就是说,有些时候可能出现的异常是我们处理不了的,这个时候该怎么办呢?针对这种情况,Java提供了throws 的处理方案
格式:
throws异常类名;
注意: 这个格式是跟在方法的括号后面的
- 编译时异常必须要进行处理,两种处理方案: try…catch …或者throws,如果采用throws这种方案,将来淮调用谁处理
- 运行时异常可以不处理,出现问题后,需要我们回来修改代码
格式:
public class 异常类名 extends Exception { 无参构造 带参构造 }
-
throws
用在方法声明后面,跟的是异常类名
表示抛出异常,由该方法的调用者来处理
表示出现异常的一种可能性,并不一定会发生这些异常 -
throw
用在方法体内,跟的是异常对象名
表示抛出异常,由方法体内的语句处理
执行throw一定抛出了某种异常
范例:
public void checkScore(int score) throws ScoreException{
if(score<0 || score>180) {
throw new ScoreException("你给的分数有误,分数应该在0-100之间");
}else {
System.out.println("分数正常");
}
集合进阶
1.Collection
1.1 集合类的特点
提供一种存储空间可变的存储模型,存储的数据容量可以随时发生改变
集合类体系结构
1.2 Collection集合概述- 是单例集合的顶层接口,它表示一组对象,这些对象也称为Collection的元素
- JDK不提供此接口的任何直接实现,它提供更具体的子接口(如Set和List)实现
- 多态的方式 Collection
c = new ArrayList (); - 具体的实现类ArrayList
| 方法名 | 说明 |
|---|---|
| boolean add(E e) | 添加元素 |
| boolean remove(Object o) | 从集合中移除指定的元素 |
| void clear() | 清空集合中的元素 |
| boolean contains(Object o) | 判断集合中是否存在指定的元素 |
| boolean isEmpty() | 判断集合是否为空 |
| int size() | 集合的长度,也就是集合中元素的个数 |
ALt+7打开一个窗口,能够看到类的所有信息.
1.5 Collection集合的遍历 lterator:迭代器,集合的专用遍历方式
-
lterator iterator():返回此集合中元素的迭代器,通过集合的iterator()方法得到 Iterator
it = c.iterator () ; -
迭代器是通过集合的iterator)方法得到的,所以我们说它是依赖于集合而存在的
lterator中的常用方法
- E next():返回迭代中的下一个元素
- boolean hasNext():如果迭代具有更多元素,则返回true
- 有序集合(也称为序列),用户可以精确控制列表中每个元素的插入位置。用户可以通过整数索引访问元素,并搜索列表中的元素
- 与Set集合不同,列表通常允许重复的元素
List集合特点
- 有序:存储和取出的元素顺序一致
- 可重复:存储的元素可以重复
| 方法名 | 说明 |
|---|---|
| void add(int index,E element) | 在此集合中的指定位置插入指定的元素 |
| E remove(int index) | 删除指定索引处的元素,返回被删除的元素 |
| E set(int index,E element) | 修改指定索引处的元素,返回被修改的元素 |
| E get(int index) | 返回指定索引处的元素 |
并发修改异常:ConcurrentModificationException
产生原因:迭代器遍历的过程中,通过集合对象修改了集合中元素的长度,造成了迭代器获取元素中判断预期修改值和实际修改值不一致
解决方案:用for循环遍历,然后用集合对象做对应的操作即可
2.4 ListlteratorListlterator:列表迭代器
- 通过List集合的listlterator()方法得到,所以说它是List集合特有的迭代器
- 用于允许程序员沿任一方向遍历列表的列表迭代器,在迭代期间修改列表,并获取列表中迭代器的当前位置
Listlterator中的常用方法
- E next():返回迭代中的下一个元素
- boolean hasNext():如果迭代具有更多元素,则返回true
- E previous():返回列表中的上一个元素
- boolean hasPrevious():如果此列表迭代器在相反方向遍历列表时具有更多元素,则返回
- truevoid add(E e):将指定的元素插入列表
增强for:简化数组和Collection集合的遍历
- 实现lterable接口的类允许其对象成为增强型for语句的目标
- 它是JDK5之后出现的,其内部原理是一个lterator迭代器
增强for的格式:
for(元素数据类型 变量名:数组或者Collection集合){
//在此处使用变量即可,该变量就是元素
}
范例:
int[] arr = {1,2,3,4,5};
for(int i : arr){
System.out.println(i);
}
2.6 数据结构
数据结构是计算机存储、组织数据的方式。是指相互之间存在一种或多种特定关系的数据元素的集合通常情况下,精心选择的数据结构可以带来更高的运行或者存储效率。
2.7 常见的数据结构之栈数据进入栈模型的过程称为:压/进栈
数据离开栈模型的过程称为:弹/出栈
栈是一种数据先进后出的模型
2.8 常见的数据结构之队列数据从后端进入队列模型的过程称为:入队列
数据从前端离开队列模型的过程称为:出队列
队列是一种数据先进先出的模型
2.9 常见的数据结构之数组查询数据通过索引定位,查询任意数据耗时相同,查询速度快
删除数据时,要将原始数据删除,同时后面每个数据前移,删除效率低
添加数据时,添加位置后的每个数据后移,再添加元素,添加效率极低
数组是一种查询快,增删慢的模型
2.10 常见的数据结构之链表链表是一种增删快的模型(对比数组)
链表是一种查询慢的模型(对比数组)
2.11 List集合子类特点List集合常用子类:ArrayList,linkedList
- ArrayList:底层数据结构是数组,查询快,增删慢
- linkedList:底层数据结构是链表,查询慢,增删快
| 方法名 | 说明 |
|---|---|
| public void addFirst(E e) | 在该列表开头插入指定的元素 |
| public void addLast(E e) | 将指定的元素追加到此列表的末尾 |
| public E getFirst() | 返回此列表中的第一个元素 |
| public E getLast() | 返回此列表中的最后一个元素 |
| public E removeFirst() | 从此列表中删除并返回第一个元素 |
| public E removeLast() | 从此列表中删除并返回最后一个元素 |
Set集合特点
- 不包含重复元素的集合
- 没有带索引的方法,所以不能使用普通for循环遍历
Set集合练习
- 存储字符串并遍历
哈希值:是JDK根据对象的地址或者字符串或者数字算出来的int类型的数值
Object类中有一个方法可以获取对象的哈希值
- public int hashCode():返回对象的哈希码值
对象的哈希值特点
- 同一个对象多次调用hashCode0方法返回的哈希值是相同的
- 默认情况下,不同对象的哈希值是不同的。而重写hashCode()方法,可以实现让不同对象的哈希值相同
HashSet集合特点
- 底层数据结构是哈希表
- 对集合的迭代顺序不作任何保证,也就是说不保证存储和取出的元素顺序一致
- 没有带索引的方法,所以不能使用普通for循环遍历
- 由于是Set集合,所以是不包含重复元素的集合
HashSet集合练习
- 存储字符串并遍历
HashSet集合添加一个元素的过程:
HashSet集合存储元素:
- 要保证元素唯一性,需要重写hashCode()和equals()
哈希表,JDK8之前,底层采用数组+链表实现,可以说是一个元素为链表的数组,JDK8以后,在长度比较长的时候,底层实现了优化
3.6 linkedHashSet集合概述和特点linkedHashSet集合特点
- 哈希表和链表实现的Set接口,具有可预测的迭代次序
- 由链表保证元素有序,也就是说元素的存储和取出顺序是一致的
- 由哈希表保证元素唯一,也就是说没有重复的元素
3.7 TreeSet集合概述和特点TreeSet集合特点
-
元素有序,这里的顺序不是指存储和取出的顺序,而是按照一定的规则进行排序,具体排序方式取决于构造方法
TreeSet():根据其元素的自然非序进行排序
TreeSet(Comparator comparator):根据指定的比较器进行排序
-
没有带索引的方法,所以不能使用普通for循环遍历
-
由于是Set集合,所以不包含重复元素的集合
集合存储基本数据类型的方法:使用包装类把基本类型包装成引用类型,这样集合就可以存储了。
3.8自然排序Comparable的使用存储学生对象并遍历,创建TreeSet集合使用无参构造方法
要求:按照年龄从小到大排序,年龄相同时,按照姓名的字母顺序排序
结论
-
用TreeSet集合存储自定义对象,无参构造方法使用的是自然排序对元素进行排序的
-
自然排序,就是让元素所属的类实现Comparable接口,重写compareTo(To)方法
-
重写方法时,一定要注意排序规则必须按照要求的主要条件和次要条件来写
存储学生对象并遍历,创建TreeSet集合使用带参构造方法
要求:按照年龄从小到大排序,年龄相同时,按照姓名的字母顺序排序
结论
- 用TreeSet集合存储自定义对象,带参构造方法使用的是比较器排序对元素进行排序的
- 比较器排序,就是让集合构造方法接收Comparator的实现类对象,(可以使用匿名内部类),重写compare(T o1,T o2)方法
- 重写方法时,一定要注意排序规则必须按照要求的主要条件和次要条件来写
泛型:是JDK5中引入的特性,它提供了编译时类型安全检测机制,该机制允许在编译时检测到非法的类型,它的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数
一提到参数,最熟悉的就是定义方法时有形参,然后调用此方法时传递实参。那么参数化类型怎么理解呢?
顾名思义,就是将类型由原来的具体的类型参数化,然后在使用/调用时传入具体的类型。这种参数类型可以用在类、方法和接口中,分别被称为泛型类、泛型方法、泛型接口。
泛型定义格式:
- <类型>:指定一种类型的格式。这里的类型可以看成是形参
- <类型1,类型2.…>∶指定多种类型的格式,多种类型之间用逗号隔开。这里的类型可以看成是形参
- 将来具体调用时候给定的类型可以看成是实参,并且实参的类型只能是引用数据类型
泛型的好处:
- 把运行时期的问题提前到了编译期间
- 避免了强制类型转换
泛型类的定义格式:修饰符 class 类名<类型> {}
范例: public class Generic
此处T可以随便写为任意标识,常见的如T、E、K、V等形式的参数常用于表示泛型
泛型方法的定义格式: 修饰符 <类型> 返回值类型 方法名(类型变量名) {}
范例:public
泛型接口的定义格式: 修饰符interface接口名<类型>{ }
范例: public interface Generic
为了表示各种泛型List的父类,可以使用类型通配符
- 类型通配符:>
- List>:表示元素类型未知的List,它的元素可以匹配任何的类型
- 这种带通配符的List仅表示它是各种泛型List的父类,并不能把元素添加到其中
如果说我们不希望List>是任何泛型L ist的父类,只希望它代表某一类泛型list的父类,可以使用类型通配符的上限
- 类型通配符上限: extends类型>
- List extends Number> :它表示的类型是Number或者其子类型
除了可以指定类型通配符的上限,我们也可以指定类型通配符的下限
- 类型通配符下限: super类型>
- List super Number> :它表示的类型是Number或者其父类型
可变参数又称参数个数可变,用作方法的形参出现,那么方法参数个数就是可变的
格式: 修饰符返回值类型方法名(数据类型...变量名){}
范例: public static int sum(int... a){ }
可变参数注意事项
- 这里的变量其实是一个数组
- 如果一个方法有多个参数,包含可变参数,可变参数要放在最后
Arrays工具类中有一个静态方法:
- public static
List asList(... a):返回由指定数组支持的固定大小的列表 - 返回的集合不能做增删操作,可以做修改操作
List接口中有一个静态方法:
- public static
List of(E... elements):返回包含任意数呈元素的不可变列表 - 返回的集合不能做增删改操作
Set接口中有一个静态方法:
- public static
Set of(E... elements):返回一个包含任意数量元素的不可变集合 - 返回的集合不能做增删操作,没有修改方法
Map集合概述
- lnterface Map
K:键的类型; V:值的类型 - 将键映射到值的对象;不能包含重复的键,键相同值覆盖;每个键可以映射到最多一个值
- 举例: 学生的学号和姓名
itheima001 林青霞
itheima002 张曼玉
itheima003 王祖贤
创建Map集合的对象
- 多态的方式
- 具体的实现类HashMap
| 方法名 | 说明 |
|---|---|
| V put(K key,V value) | 添加元素 |
| V remove(Object key) | 根据键删除键值对元素 |
| void clear() | 移除所有的键值对元素 |
| boolean containsKey(Object key) | 判断集合是否包含指定的键 |
| boolean containsValue(Object value) | 判断集合是否包含指定的值 |
| boolean isEmpty() | 判断集合是否为空 |
| int size() | 集合的长度,也就是集合中键值对的个数 |
| 方法名 | 说明 |
|---|---|
| V get(Object key) | 根据键获取值 |
| Set keySet() | 获取所有键的集合 |
| Collection values() | 获取所有值的集合 |
| Set | 获取所有键值对对象的集合 |
我们刚才存储的元素都是成对出现的,所以我们把Map看成是一个夫妻对的集合
方式一:遍历思路
- 把所有的丈夫给集中起来
- 遍历丈夫的集合,获取到每一个丈夫
- 根据丈夫去找对应的妻子
转换为Map集合中的操作:
- 获取所有键的集合。用keySet()方法实现
- 遍历键的集合,获取到每一个键。用增强for实现
- 根据键去找值。用get(Object key)方法实现
方式二:遍历思路
- 获取所有结婚证的集合
- 遍历结婚证的集合,得到每一个结婚证
- 根据结婚证获取丈夫和妻子
转换为Map集合中的操作:
-
获取所有键值对对象的集合
Set
> entrySet():获取所有键值对对象的集合 -
遍历键值对对象的集合,得到每一个键值对对象
用增强for实现,得到每一个Map.Entry
-
根据键值对对象获取键和值
用getKey()得到键用getValue0得到值
Collections类的概述
- 是针对集合操作的工具类
Collections类的常用方法
- public static
> void sort(List list):将指定的列表按升序排序 - public static void reverse(List> list):反转指定列表中元素的顺序
- public static void shuffle(List> list):使用默认的随机源随机排列指定的列表
File:它是文件和目录路径名的抽象表示
-
文件和目录是可以通过File封装成对象的
-
对于file而言,其封摸的并不是一个真正存在的文件,仅仅是一个路径名而已。它可以是存在的,也可以是不存在的。将来是要通过具体的操作把这个路径的内容转换为具体存在的
| 方法名 | 说明 |
|---|---|
| File(String pathname) | 通过将给定的路径名字符串转换为抽象路径名来创建新的File实例 |
| File(String parent, String child) | 从父路径名字符串和子路径名字符串创建新的File实例 |
| File(File parent, String child) | 从父抽象路径名和子路径名字符串创建新的File实例 |
| 方法名 | 说明 |
|---|---|
| public boolean createNewFile() | 当具有该名称的文件不存在时, 创建一个由该抽象路径名命名的新空文件 |
| public boolean mkdir() | 创建由此抽象路径名命名的目录 |
| public boolean mkdirs() | 创建由此抽象路径名命名的目录,包括任何必需但不存在的父目录 |
| 方法名 | 说明 |
|---|---|
| public boolean isDirectory() | 测试此抽象路径名表示的File是否为目录 |
| public boolean isFile() | 测试此抽象路径名表示的File是否为文件 |
| public boolean exists() | 测试此抽象路径名表示的File是否存在 |
| public String getAbsolutePath() | 返回此抽象路径名的绝对路径名字符串 |
| public String getPath() | 将此抽象路径名转换为路径名字符串 |
| public String getName() | 返回由此抽象路径名表示的文件或目录的名称 |
| public StringI] list() | 返回此抽象路径名表示的目录中的文件和目录的名称字符串数组 |
| public File[ listFiles() | 返回此抽象路径名表示的目录中的文件和目录的File对象数组 |
| 方法名 | 说明 |
|---|---|
| public boolean delete() | 删除由此抽象路径名 表示的文件或目录 |
绝对路径和相对路径的区别
- 绝对路径:完整的路径名,不需要任何其他信息就可以定位它所表示的文件。例如:E:\itcast\java.txt
- 相对路径: 必须使用取自其他路径名的信息进行解释。例如: myFile\java.txt
删除目录时的注意事项
- 如果一个目录中有内容(目录, 文件),不能直接删除。应该先删除目录中的内容,最后才能删除目录
递归指的是方法定义中调用方法本身的现象。
递归解决问题的思路:
把一个复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解
递归策略只需少量的程序就可描述出解题过程所需要的多次重复计算
递归解决问题要找到两个内容:
- 递归出口: 否则会出现内存溢出
- 递归规则: 与原问题相似的规模较小的问题
IO流概述:
- IO: 输入/输出(Input/Output)
- 流: 是一种抽象概念,是对数据传输的总称。也就是说数据在设备间的传输称为流,流的本质是数据传输
- IO流就是 用来处理设备间数据传输问题的
常见的应用:文件复制;文件上传;文件下载
IO流分类:
-
按照数据的流向
输入流:读数据
输出流:写数据 -
按照数据类型来分
字节流
字节输入流;字节输出流
字符流
字符输入流;字符输出流
如果数据通过Window自带的记事本软件打开,我们还可以读懂里面的内容,就使用字符流,否则使用字节流。如果你不知道该使用哪种类型的流,就使用字节流
2.2字节流写数据字节流抽象基类
- lnputStream:这个抽象类是表示字节输入流的所有类的超类
- OutputStream:这个抽象类是表示字节输出流的所有类的超类
- 子类名特点:子类名称都是以其父类名作为子类名的后缀
FileOutputStream:文件输出流用于将数据写入File
- FileOutputStream(String name):创建文件输出流以指定的名称写入文件
使用字节输出流写数据的步骤:
- 创建字节输出流对象(调用系统功能创建了文件,创建字节输出流对象,让字节输出流对象指向文件)
- 调用字节输出流对象的写数据方法
- 释放资源(关闭此文件输出流并释放与此流相关联的任何系统资源)
| 方法名 | 说明 |
|---|---|
| void write(int b) | 将指定的字节写入此文件输出流,一次写一 个字节数据 |
| void wite(byte[] b) | 将b.length字节从指定的字节数组写入此文件输出流,一次写一个字节数组数据 |
| void write(byte[]b, int off, int len) | 将 len字节从指定的字节数组开始,从偏移量off开始写入此文件输出流,一次写一个字节数组的部分数据 |
字节流写数据如何实现换行呢?
-
写完数据后,加换行符
windows:rn
linux:n
mac:r
字节流写数据如何实现追加写入呢?
- public FileOutputStream(String name,boolean append):创建文件输出流以指定的名称写入文件。如果第二个参数为true,则字节将写入文件的末尾而不是开头
finally:在异常处理时提供finally块来执行所有清除操作。比如说IO流中的释放资源
特点:被finally控制的语句一定会执行,除非JVM退出
`try {
可能出现异常的代码;
}catch(异常类名变量名){
异常的处理代码;
}finally{
执行所有清除操作;
}`
2.6字节流读数据(一次读一个字节数据)
需求:把文件fos.txt中的内容读取出来在控制台输出
FilelnputStream:从文件系统中的文件获取输入字节
- FilelnputStream(String name):通过打开与实际文件的连接来创建一个FileInputStream,该文件由文件系统中的路径名name命名
字节缓冲流:
- BufferOutputStream:该类实现缓冲输出流。通过设置这样的输出流,应用程序可以向底层输出流写入字节,而不必为写入的每个字节导致底层系统的调用
- BufferedInputStream:创建Bufferedlnputstream将创建一个内部缓冲区数组。当从流中读取或跳过字节时,内部缓冲区将根据需要从所包含的输入流中重新填充,一次很多字节
构造方法:
- 字节缓冲输出流:BufferedOutputStream(OutputStream out)
- 字节缓冲输入流:BufferedInputStream(InputStream in)
由于字节流操作中文不是特别的方便,所以Java就提供字符流
- 字符流=字节流+编码表
用字节流复制文本文件时,文本文件也会有中文,但是没有问题,原因是最终底层操作会自动进行字节拼接成中文,如何识别是中文的呢?
- 汉字在存储的时候,无论选择哪种编码存储,第一个字节都是负数
基础知识:
- 计算机中储存的信息都是用二进制数表示的;我们在屏幕上看到的英文、汉字等字符是二进制数转换之后的结果
- 按照某种规则,将字符存储到计算机中,称为编码。反之,将存储在计算机中的二进制数按照某种规则解析显示出来,称为解码。这里强调一下:按照A编码存储,必须按照A编码解析,这样才能显示正确的文本符号。否则就会导致乱码现象
字符编码:就是一套自然语言的字符与二进制数之间的对应规则(A:65)
字符集:
- 是一个系统支持的所有字符的集合,包括各国家文字、标点符号、图形符号、数字等
- 计算机要准确的存储和识别各种字符集符号,就需要进行字符编码,一套字符集必然至少有一套字符编码。常见字符集有ASCII字符集、GBXXX字符集、Unicode字符集等
ASCll字符集:
- ASCIi(American Standard Code for Information Interchange,美国信息交换标准代码):是基于拉丁字母的一套电脑编码系统,用于显示现代英语,主要包括控制字符(回车键、退格、换行键等)和可显示字符(英文大小写字符、阿拉伯数字和西文符号)
- 基本的ASCII字符集,使用7位表示一个字符,共128字符。ASCII的扩展字符集使用8位表示一个字符,共256字符,方便支持欧洲常用字符。是一个系统支持的所有字符的集合,包括各国家文字、标点符号、图形符号、数字等
GBXXX字符集:
- GB2312:简体中文码表。一个小于127的字符的意义与原来相同,但两个大于127的字符连在一起时,就表示一个汉字,这样大约可以组合了包含7000多个简体汉字,此外数学符号、罗马希腊的字母、日文的假名等都编进去了,连在ASCII里本来就有的威字、标点、字母都统统重新编了两个字节长的编码,这就是常说的"全角"字符,而原来在127号以下的那些就叫"半角"字符了
- GBK:最常用的中文码表。是在GB2312标准基础上的扩展规范,使用了双字节编码方案,共收录了21003个汉字,完全兼容GB2312标准,向时支持繁体汉字以及日韩汉字等
- GB18030:最新的中文码表。收录汉字70244个,采用多字节编码,每个字可以由1个、2个或4个字节组成。支持中国国内少数民族的文字,同时支持繁体汉字以及日韩汉字等
Unicode字符集:
- 为表达任意语言的任意字符而设计,是业界的一种标准,也称为统一码、标准万国码。它最多使用4个字节的数字来表达每个字母、符号,或者文字。有三种编码方案,UTF-8、UTF-16和UTF32。最为常用的UTF-8编码
- UTF-8编码:可以用来表示Unicode标准中任意字符,它是电子邮件、网页及其他存储或传送文字的应用中,优先采用的编码。互联网工程工作小组(IETF)要求所有互联网协议都必须支持UTF-8编码。它使用—至四个字节为每个字符编码
编码规则:
128个US-ASCII字符,只需一个字节编码
拉丁文等字符,需要二个字节编码
大部分常用字(含中文),使用三个字节编码
其他极少使用的Unicode辅助衣符,使用四字节编码
小结:采用何种规则编码,就要采用对应规则解码,否则就会出现乱码
3.3字符串中的编码解码问题使用何种字符集编码,就要使用相同的字符集解码
3.4字符流中的编码解码问题字符流抽象基类
- Reader:字符输入流的抽象类
- Writer:字符输出流的抽象类
字符流中和编码解码问题相关的两个类:
- lnputStreamReader
- OutputStreamWriter
| 方法名 | 说明 |
|---|---|
| void write(int c) | 写一个字符 |
| void write(char[] cbuf) | 写入一个字符数组 |
| void write(char[] cbuf, int off, int len) | 写入字符数组的一部分 |
| void write(String str) | 写一个字符串 |
| void write(String str, int off, int len) | 写-个字符串的一部分 |
| 方法名 | 说明 |
|---|---|
| flush() | 刷新流,还可以继续写数据 |
| close() | 关闭流,释放资源,但是在关闭之前会先刷新流。一-旦关闭,就不能再写数据 |
| 方法名 | 说明 |
|---|---|
| int read() | 一次读一 个字符数据 |
| int read(char[] cbuf) | 一次读一 个字符数组数据 |
字符缓冲流:
- BufferedWriter:将文本写入字符输出流,缓冲字符,以提供单个字符,数组和字符串的高效写入,可以指定缓冲区大小,或者可以接受默认大小。默认值足够大,可用于大多数用途
- BufferedReader:从字符输入流读取文本,缓冲字符,以提供字符,数组和行的高效读取,可以定缓冲区大小,或者可以使用默认大小。默认值足够大,可用于大多数用途
构造方法:
- BufferedWriter(Writer out)
- BufferedReader(Reader in)
BufferedWriter:
- void newline(): 写一行行分隔符,行分隔符字符串由系统属性定义
BufferedReader:
- public String readLine():读一行文字。结果包含行的内容的字符串,不包括任何行终止字符,如果流的结尾已经到达,则为null
方式一:直接抛出处理,关注点在执行流程上
方式二:try{…}catch{…}finally{…}
try { 可能出现异常的代码; }catch(异常类名 变量名){ 异常的处理代码; }finally{ 执行所有清除操作; (还需要做try{...}catch{...}异常操作) }
方式三:JDK7(自动释放资源)
try (定义流对象){ 可能出现异常的代码; }catch(异常类名 变量名){ 异常的处理代码; }
方式四:JDK9(自动释放资源)
定义输入流对象; 定义输出流对象;
try (输入流对象;输出流对象){ 可能出现异常的代码; }catch(异常类名 变量名){ 异常的处理代码; }
System类中有两个静态的成员变量:
- public static final lnputStream in:标准输入流。通常该流对应于键盘输入或由主机环境或用户指定的另一个输入源
- public static final PrintStream out:标准输出流。通常该流对应于显示输出或由主机环境或用户指定的另一个输出目标
自己实现键盘录入数据:
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
写起来太麻烦,就提供了一个类实现键盘录入
Scanner sc = new Scanner(System.in);
System类中有两个静态的成员变量:
- public static final lnputStream in:标准输入流。通常该流对应于键盘输入或由主机环境或用户指定的另一个输入源
- public static final PrintStreamout:标准输出流。通常该流对应于显示输出或由主机环境或用户指定的另一个输出目标
输出语句的本质:是一个标准的输出流
- PrintStream ps = System.out;
- PrintStream类有的方法,System.out都可以使用
打印流分类:
- 字节打印流:PrintStream
- 字符打印流:PrintWriter
打印流的特点:
- 只负责输出数据,不负责读取数据
- 有自己的特有方法
字节打印流
- PrintStream(String fileName):使用指定的文件名创建新的打印流
- 使用继承父类的方法写数据,查看的时候会转码;使用自己的特有方法写数据,查看的数据原样输出
字符打印流PrintWriter的构造方法:
| 方法名 | 说明 |
|---|---|
| PrintWriter(String fileName) | 使用指定的文件名创建一个 新的PrintWriter,而不需要自动执行局新 |
| PrintWriter(Writer out, boolean autoFlush) | 创建一个新的PrintWriter ●out:字符输出流 ●autoFlush:一个布尔值,如果为真,则println, printf, 或format方法将刷新输出缓冲区 |
对象序列化:就是将对象保存到磁盘中,或者在网络中传输对象
这种机制就是使用一个字节序列表示一个对象,该字节序列包含:对象的类型、对象的数据和对象中存储的属性等信息字节序列写到文件之后,相当于文件中持久保存了一个对象的信息
反之,该字节序列还可以从文件中读取回来,重构对象,对它进行反序列化
要实现序列化和反序列化就要使用对象序列化流和对象反序列化流:
- 对象序列化流:ObjectOutputStream
- 对象反序列化流:ObjectInputStream
对象序列化流:ObjectOutputStream
- 将Java对象的原始数据类型和图形写入OutputStream。可以使用ObjectIlnputStream读取(重构)对象。可以通过使用流的文件来实现对象的持久存储。如果流是网络套接字流,则可以在另一个主机上或另一个进程中重构对象
构造方法:
- ObjectOutputStream(OutputStream out):创建一个写入指定的OutputStream的ObjectOutputStream
序列化对象的方法:
- void writeObject(Object obj):将指定的对象写入ObjectOutputStream
注意:
- 一个对象要想被序列化, 该对象所属的类必须实现Serializable 接口
- Serializable是一 个标记接口,实现该接口,不需要重写任何方法
对象反序列化流:ObjectInputStream
- ObjectlnputStream反序列化先前使用ObjectOutputStream编写的原始数据和对象
构造方法:
- ObjectInputStream(InputStreamin): 创建从指定的InputStream读取的ObjectlnputStream
反序列化对象的方法:
- ObjectreadObject(): 从ObjectInputStream读取一个对象
用对象序列化流序列化了一个对象后, 假如我们修改了对象所属的类文件,读取数据会不会出问题呢?
- 会出问题, 抛出InvalidClassException异常
如果出问题了,如何解决呢?
- 给对象所属的类加一个serialVersionUID
private static final long serialVersionUID = 42L;
如果一个对象中的某个成员变量的值不想被序列化,又该如何实现呢?
- 给该成员变量加transient关键字修饰,该关键字标记的成员变量不参与序列化过程
Properties概述:
-
是一个Map体系的集合类(不加泛型)
-
Properties可以保存到流中或从流中加载
Properties作为集合的特有方法:
| 方法名 | 说明 |
|---|---|
| Object setProperty(String key, String value) | 设置集合的键和值,都是String类型,底层调用Hashtable方法put |
| String getProperty(String key) | 使用此属性列表中指定的键搜索属性 |
| Set< String> stringPropertyNames() | 从该属性列表中返回一个不可修改的键集,其中键及其对应的值是字符串 |
Properties和IO流结合的方法:
| 方法名 | 说明 |
|---|---|
| void load(InputStream inStream) | 从输入字节流读取属性列表(键和元素对) |
| void load(Reader reader) | 从输入字符流读取属性列表(键和元素对) |
| void store(OutputStream out, String comments) | 将此属性列表(键和元素对)写入此Properties表中,以适合于使用load(InputStream)方法的格式写入输出字节流 |
| void store(Writer writer, String comments) | 将此属性列表(键和元索对)写入此Properties表中,以适合使用load(Reader)方法的格式写入输出字符流 |
进程:是正在运行的程序
- 是系统进行资源分配和调用的独立单位
- 每一个进程都有它自己的内存空间和系统资源
线程:是进程中的单个顺序控制流,是一条执行路径
- 单线程:一个进程如果只有一条执行路径,则称为单线程程序
- 多线程:一个进程如果有多条执行路径,则称为多线程程序
方式1:继承Thread类
定义一个类MyThread继承Thread类 在MyThread类中重写run()方法 创建MyThread类的对象 启动线程
为什么要重写run()方法?
- 因为run()是用来封装被线程执行的代码
run()方法和start()方法的区别?
- run():封装线程执行的代码,直接调用,相当于普通方法的调用
- start(): 启动线程;然后由JVM调用此线程的run()方法
Thread类中设置和获取线程名称的方法:
- void setName(String name):将此线程的名称更改为等于参数name
- String getName():返回此线程的名称
线程有两种调度模型
- 分时调度模型:所有线程轮流使用CPU的使用权,平均分配每个线程占用CPU的时间片
- 抢占式调度模型:优先让优先级高的线程使用CPU,如果线程的优先级相同,那么会随机选择一个,优先级高的线程获取的CPU时间片相对多一些
Java使用的是抢占式调度模型
假如计算机只有一个CPU,那么CPU在某一个时刻只能执行一条指令,线程只有得到CPU时间片,也就是使用权,才可以执行指令。所以说多线程程序的执行是有随机性,因为谁抢到CPU的使用权是不一定的
Thread类中设置和获取线程优先级的方法
- public final int getPriority():返回此线程的优先级
- public final void setPriority(int newPriority):更改此线程的优先级
线程默认优先级是5;线程优先级的范围是:1-10,越大越优先
线程优先级高仅仅表示线程获取的CPU时间片的几率高,但是要在次数比较多,或者多次运行的时候才能看到你想要的效果
1.6线程控制| 方法名 | 说明 |
|---|---|
| static void sleep(long millis) | 使当前正在执行的线程停留 (暂停执行)指定的亳秒数 |
| void join() | 等待这个线程死亡 |
| void setDaemon(boolean on) | 将此线程标记为守护线程, 当运行的线程都是守护线程时,Java虚拟机将退出 |
方式二:实现Runnable接口
定义一个类MyRunnable实现Runnable接口 在MyRunnable类中重写run()方法 创建MyRunnable类的对象 创建Thread类的对象,把MyRunnable对象作为构造方法的参数 启动线程
- 避免了Java单继承的局限性
- 适合多个相同程序的代码去处理同一个资源的情况,把线程和程序的代码、数据有效分离,较好的体现了面向对象的设计思想
锁多条语句操作共享数据,可以使用同步代码块实现
-
格式: synchronized(任意对象){ 多条语句操作共享数据的代码 }
-
synchronized(任意对象):就相当于给代码加锁了,任意对象就可以看成是一把锁
同步的好处和弊端
- 好处:解决了多线程的数据安全问题
- 弊端;当线程很多时,因为每个线程都会去判断同步上的锁,这是很耗费资源的,无形中会降低程序的运行效率
同步方法:就是把synchronized关键字加到方法上
- 格式:
修饰符synchronized返回值类型方法名(方法参数){ }
同步方法的锁对象是什么呢?
- this
同步静态方法:就是把synchronized关键字加到静态方法上
- 格式:
修饰符static synchronized返回值类型方法名(方法参数){}
同步静态方法的锁对象是什么呢?
- 类名.class
StringBuffer
- 线程安全,可变的字符序列
- 从版本JDK5开始,被StringBuilder替代。通常应该使用StringBuilder类,因为它支持所有相同的操作,但它更快,因为它不执行同步
Vector
- 从Java 2平台v1.2开始,该类改进了List接口,使其成为JavaCollectionsframework的成员。与新的集合实现不同,Vector被同步。如果不需要线程安全的实现。建议使用ArrayList代替Vector
Hashtable
- 该类实现了一个哈希表,它将键映射到值。任何非null对象都可以用作键或者值
- 从Java 2平台v1.2开始,该类进行了改进,实现了Map接口,使其成为JavaCollections framework的成员。与新的集合实现不同,Hashtable被同步。如果不需要线程安全的实现,建议使用HashMap代替Hashtable
虽然我们可以理解同步代码块和同步方法的锁对象问题,但是我们并没有直接看到在哪里加上了锁,在哪里释放了锁,为了更清晰的表达如何加锁和释放锁,JDK5以后提供了一个新的锁对象Lock
Lock实现提供比使用synchronized方法和语句可以获得更广泛的锁定操作
Lock中提供了获得锁和释放锁的方法
- void lock():获得锁
- void unlock():释放锁
Lock是接口不能直接实例化,这里采用它的实现类ReentrantLock来实例化
ReentrantLock的构造方法
- ReentrantLock():创建一个ReentrantLock的实例
生产者消费者模式是一个十分经典的多线程协作的模式,弄懂生产者消费者问题能够让我们对多线程编程的理解更加深刻所谓生产者消费者问题,实际上主要是包含了两类线程:
- 一类是生产者线程用于生产数据
- 一类是消费者线程用于消费数据
为了解耦生产者和消费者的关系,通常会采用共享的数据区域,就像是一个仓库
- 生产者生产数据之后直接放置在共享数据区中,并不需要关心消费者的行为
- 消费者只需要从共享数据区中去获取数据,并不需要关心生产者的行为
为了体现生产和消费过程中的等待和唤醒,Java就提供了几个方法供我们使用,这几个方法在Object类中Object类的等待和唤醒方法:
| 方法名 | 说明 |
|---|---|
| void wait() | 导致当前线程等待,直到另-个线程调用该对象的notify()方法或notifyAll()方法 |
| void notify() | 唤醒正在等待对象监视器的单个线程 |
| void notifyAll() | 唤醒正在等待对象监视器的所有线程 |
生产者消费者案例中包含的类:
-
奶箱类(Box):定义一个成员变量,表示第x瓶奶,提供存储牛奶和获取牛奶的操作
-
生产者类(Producer):实现Runnable接口,重写run0方法,调用存储牛奶的操作
-
消费者类(Customer):实现Runnable接口,重写run)方法,调用获取牛奶的操作
-
测试类(BoxDemo):里面有main方法,main方法中的代码步骤如下
- 创建奶箱对象,这是共享数据区域
- 创建生产者对象,把奶箱对象作为构造方法参数传递,因为在这个类中要调用存储牛奶的操作
- 创建消费者对象,把奶箱对象作为构造方法参数传递,因为在这个类中要调用获取牛奶的操作
- 创建2个线程对象,分别把生产者对象和消费者对象作为构造方法参数传递
- 启动线程
计算机网络
- 是指将地理位 置不同的具有独立功能的多台计算机及其外部设备,通过通信线路连接起来,在网络操作系统,网络管理软件及网络通信协议的管理和协调下,实现资源共享和信息传递的计算机系统
网络编程
- 在网络通信协议下,实现网络互连的不同计算机上运行的程序间可以进行数据交换
IP地址
- 要想让网络中的计 算机能够互相通信,必须为每台计算机指定-个标识号, 通过这个标识号来指定要接收数
据的计算机和识别发送的计算机,而IP地址就是这个标识号。也就是设备的标识
端口
- 网络的通信, 本质上是两个应用程序的通信。每台计算机都有很多的应用程序,那么在网络通信时,如何区分这些应用程序呢?如果说IP地址可以唯一标识网络中的设备 ,那么端口号就可以唯一标识设备中的应用程序了。也就是应用程序的标识
协议
- 通过计算机网络可以使多台计算机实现连接,位于同-个网络中的计算机在进行连接和通信时需要遵守一定的规则,这就好比在道路中行驶的汽车一定要遵守交通规则- 样。在计算机网络中,这些连接和通信的规则被称为网络通信协议,它对数据的传输格式、传输速率、传输步骤等做了统一 规定,通信双方必须同时遵守才能完成数据交换。常见的协议有UDP协议和TCP协议
IP地址:是网络中设备的唯一标识
IP地址分为两大类
- IPv4:是给每个连接在网络上的主机分配一个32bi地址。按照TCP/IP规定,IP地址用二进制来表示,每个P地址长32bit,也就是4个字节。例如一个采用二进制形式的P地址是"1000000 1010100000000001 01000010" ,这么长的地址,处理起来也太费劲了。为了方便使用,IP地址经常被写成十进制的形式,中间使用符号"." 分隔不同的字节。
于是,上面的IP地址可以表示为“192.168.1.66"。IP地址的这种表示法叫做“分十进制表示法”, 这显然比1和0容易记忆得多 - IPv6: 由于互联网的蓬勃发展,IP地址的需求量愈来愈大,但是网络地址资源有限,使得IP的分配越发紧张。了扩大地址空间,通过IPv6重新定义地址空间,采用128位地址长度,每16个字节一组,分成8组十六进制数,这样就解决了网络地址资源数量不够的问题
常用命令:
- ipconfig: 查看本机IP地址
- ping IP地址:检查网络是否连通
特殊IP地址:
- 127.0.0.1: 是回送地址,可以代表本机地址,一般用来测试使用
InetAddress:此类表示Internet协议(IP)地址
| 方法名 | 说明 |
|---|---|
| static InetAddress getByName(String host) | 确定 主机名称的IP地址。主机名称可以是机器名称,也可以是P地址 |
| String getHostName() | 获取此IP地址的主机名 |
| String getHostAddress() | 返回文本显示中的IP地址字符串 |
端口:设备上应用程序的唯一标识
端口号:用两个字节表示的整数,它的取值范围是0-65535。其中,0~1023之间的端口号用于一些知名的网络服务和应用,普通的应用程序需要使用1024以上的端口号。如果端口号被另外一个服务或应用所占用,会导致当前程序启动失败
1.6协议协议:计算机网络中,连接和通信的规则被称为网络通信协议
UDP协议
-
用户数据报协议(User Datagram Protocol)
-
UDP是无连接通信协议,即在数据传输时,数据的发送端和接收端不建立逻辑连接。简单来说,当一台计算机向另外一台计算机发送数据时,发送端不会确认接收端是否存在,就会发出数据,同样接收端在收到数据时,也不会向发送端反馈是否收到数据。
由于使用UDP协议消耗资源小,通信效率高,所以通常都会用于音频、视频和普通数据的传输
-
例如视频会议通常采用UDP协议,因为这种情况即使偶尔丢失一两个数据包,也不会对接收结果产生太大影响。但是在使用UDP协议传送数据时,由于UDP的面向无连接性,不能保证数据的完整性,因此在传输重要数据时不建议使用UDP协议
TCP协议
-
传输控制协议(Transmission Control Protocol)
-
TCP协议是面向连接的通信协议,即传输数据之前,在发送端和接收端建立逻辑连接,然后再传输数据,它提供了两台计算机之间可靠无差错的数据传输。在TCP连接中必须要明确客户端与服务器端,由客户端向服务端发出连接请求,每次连接的创建都需要经过“三次握手”
-
三次握手:TCP协议中,在发送数据的准备阶段,客户端与服务器之间的三次交互,以保证连接的可靠
第一次握手,客户端向服务器端发出连接请求,等待服务器确认
第二次握手,服务器端向客户端回送一个响应,通知客户端收到了连接请求
第三次握手,客户端再次向服务器端发送确认信息,确认连接
-
完成三次握手,连接建立后,客户端和服务器就可以开始进行数据传输了。由于这种面向连接的特性,TCP协议可以保证传输数据的安全,所以应用十分广泛。例如上传文件、下载文件、浏览网页等
UDP协议是一种不可靠的网络协议,它在通信的两端各建立一个Socket对象,但是这两个Socket只是发送,接收数据的对象因此对于基于UDP协议的通信双方而言,没有所谓的客户端和服务器的概念
Java提供了DatagramSocket类作为基于UDP协议的Socket
发送数据的步骤
-
创建发送端的Socket对象(DatagramSocket)
DatagramSocket ds=new DatagramSocket();
-
创建数据,并把数据打包
byte[] bys="hello,udp,我来了".getBytes(); DatagramPacket dp=new DatagramPacket(bys,bys.length,InetAddress.getByName("半个有趣人"),10086); -
调用DatagramSocket对象的方法发送数据
ds.send(dp);
-
关闭发送端
ds.close();
接受数据的步骤:
-
创建接收端的Socket对象(DatagramSocket)
DatagramSocket ds=new DatagramSocket(10086);
-
创建一个数据包,用于接收数据
byte[] bys=new byte[1024]; DatagramPacket dp=new DatagramPacket(bys,bys.length);
-
调用DatagramSocket对象的方法接收数据
ds.receive(dp);
-
解析数据包,并把数据在控制台显示
System.out.println("数据是:"+new String(dp.getData(),0,dp.getLength())); -
关闭接收端
ds.close();
按照下面的要求实现程序
- UDP发送数据:数据来自于键盘录入,直到输入的数据是886,发送数据结束
- UDP接收数据:因为接收端不知道发送端什么时候停止发送,故采用死循环接收
TCP通信协议是一种可靠的网络协议,它在通信的两端各建立一个Socket对象,从而在通信的两端形成网络虚拟链路,—旦建立了虚拟的网络链路,两端的程序就可以通过虚拟链路进行通信
Java对基于TCP协议的的网络提供了良好的封装,使用Socket对象来代表两端的通信端口,并通过Socket产生IO流来进行网络通信Java为客户端提供Socket类,为服务器端提供了ServerSocket类
3.2TCP发送数据发送数据的步骤
- 创建客户端的Socket对象(Socket)
Socket s=new Socket("半个有趣人",10000); - 获取输出流,写数据
OutputStream os = s.getOutputStream(); os.write("hello,tcp,我来了".getBytes()); - 释放资源
s.close();
接收数据的步骤
-
创建服务器端的Socket对象(ServerSocket)
ServerSocket ss=new ServerSocket(10000); Socket s = ss.accept();
-
获取输入流,读数据,并把数据显示在控制台
InputStream is = s.getInputStream(); byte[] bys=new byte[1024]; int len=is.read(bys); String data=new String(bys,0,len); System.out.println("数据是:"+data); -
释放资源
s.close(); ss.close();
练习1
-
客户端:发送数据,接收服务器反馈
先写数据,后读数据
-
服务器:接收数据,给出反馈
先读数据,后写数据
练习2
- 客户端:数据来自于键盘录入,直到输入的数据是886,发送数据结束
- 服务器:接收到的数据在控制台输出
练习3
- 客户端:数据来自于键盘录入,直到输入的数据是886,发送数据结束
- 服务器:接收到的数据写入文本文件
练习4
- 客户端:数据来自于文本文件
- 服务器:接收到的数据写入文本文件
练习5
- 客户端:数据来自于文本文件,接收服务器反馈
- 服务器:接收到的数据写入文本文件,给出反馈
- 出现问题:程序一直等待
原因:读数据的方法是阻塞式的
解决办法:自定义结束标记;使用shutdownOutput(方法(推荐)
练习6
- 窖户端:数据来自于文本文件,接收服务器反馈
- 服务器:接收到的数据写入文本文件,给出反馈,代码用线程进行封装,为每一个客户端开启一个线程
- 格式:(形式参数)->{代码块}
- 形式参数:如果有多个参数,参数之间用逗号隔开;如果没有参数,留空即可
- ->:由英文中画线和大于符号组成,固定写法。代表指向动作
- 代码块:是我们具体要做的事情,也就是以前我们写的方法体内容
Lambda表达式的使用前提
- 有一个接口
- 接口中有且仅有一个抽象方法
练习1:
- 定义一个接口(Eatable),里面定义一个抽象方法: void eat();
- 定义一个测试类(EatableDemo),在测试类中提供两个方法
一个方法是: useEatable(Eatable e)
一个方法是主方法,在主方法中调用useEatable方法
练习2:
- 定义一个接口(Flyable),里面定义一个抽象方法: void fly(String s);
- 定义一个测试类(FlyableDemo),在测试类中提供两个方法
一个方法是: useFlyable(Flyable f)
一个方法是主方法,在主方法中调用useFlyable方法
练习3:
- 定义一个接口(Addable),里面定义一个抽象方法:int add(int x,int y);
- 定义一个测试类(AddableDemo),在测试类中提供两个方法
一个方法是: useAddable(Addable a)
一个方法是主方法,在主方法中调用useAddable方法
省略规则:
- 参数类型可以省略。但是有多个参数的情况下,不能只省略一个
- 如果参数有且仅有一个,那么小括号可以省略
- 如果代码块的语句只有一条,可以省略大括号和分号,甚至是return
-
使用Lambda必须要有接口,并且要求接口中有且仅有一个抽象方法
-
必须有上下文环境,才能推导出Lambda对应的接口
根据局部变量的赋值得知Lambda对应的接口:Runnable r = () -> System.out.printIn(“Lambda表达式”);
根据调用方法的参数得知Lambda对应的接口: new Thread(()-> System.out.printIn(“Lambda表达式”).start());
所需类型不同
- 匿名内部类:可以是接口,也可以是抽象类,还可以是具体类
- Lambda表达式:只能是接口
使用限制不同
- 如果接口中有且仅有一个抽象方法,可以使用Lambda表达式,也可以使用匿名内部类
- 如果接口中多于一个抽象方法,只能使用匿名内部淡,而不能使用Lambda表达式
实现原理不同
- 匿名内部类:编译之后,产生一个单独的.class字节码文件
- Lambda表达式:编译之后,没有一个单独的.class字节码文件。对应的字节码会在运行的时候动态生成
接口的组成
-
常量
public 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::printIn);
分析:直接使用System.out中的 println方法来取代Lambda,代码更加的简洁
推导与省略
- 如果使用Lambda,那么根据“可推导就是可省略”的原则,无需指定参数类型,也无需指定的重或形式,它们都将被自动推导
- 如果使用方法引用,也是同样可以根据上下文进行推导
- 方法引用是Lambda的孪生兄弟
常见的引用方式:
- 引用类方法
- 引用对象的实例方法
- 引用类的实例方法
- 引用构造器
引用类方法,其实就是引用类的静态方法
- 格式:类名::静态方法
- 范例: Integer::parselnt
Integer类的方法: public static int parselnt(String s)将此String转换为int类型数据
练习:
- 定义一个接口(Converter),里面定义一个抽象方法int convert(String s);
- 定义一个测试类(ConverterDemo),在测试类中提供两个方法
一个方法是: useConverter(Converter c)
一个方法是主方法,在主方法中调用useConverter方法
Lambda表达式被引用类方法替代的时候,它的形式参数全部传递给静态方法作为参数
1.4引用对象的实例方法引用对象的实例方法,其实就引用类中的成员方法
- 格式:对象::成员方法
- 范例:“HelloWorld”::toUpperCase
String类中的方法: public String toUpperCase()将此String所有字符转换为大写
练习
- 定义一个类(PrintString),里面定义一个方法
public void printUpper(String s):把字符串参数变成大写的数据,然后在控制台输出 - 定义一个接口(Printer),里面定义一个抽象方法void printUpperCase(Strings)
- 定义一个测试类(PrinterDemo),在测试类中提供两个方法
一个方法是: usePrinter(Printer p)
一个方法是主方法,在主方法中调用usePrinter方法
Lambda表达式被引用类方法替代的时候,它的形式参数全部传递给该方法作为参数
1.5引用类的实例方法引用类的实例方法,其实就是引用类中的成员方法
- 格式:类名::成员方法
- 范例: String::substring
String类中的方法: public String substring(int beginIndex,int endIndex)
从beginIndex开始到endlndex结束,截取字符串。返回一个子串,子串的长度为endIndex-beginIndex
练习
- 定义一个接口(MyString),里面定义一个抽象方法:
String mySubString(String s,int x,int y); - 定义一个测试类(MystringDemo),在测试类中提供两个方法
一个方法是: useMyString(MyString my)
一个方法是主方法,在主方法中调用useMyString方法
Lambda表达式被引用类方法替代的时候,第一个参数作为调用者,后面的参数全部传递给该方法作为参数
1.6引用构造器引用构造器,其实就是引用构造方法
- 格式:类名::new
- 范例:Student::new
练习
- 定义一个类(Student),里面有两个成员变量(name,age)
并提供无参构造方法和带参构造方法,以及成员变量对应的get和set方法心 - 定义一个接口(StudentBuilder),里面定义一个抽象方法
Student build(String name,int age); - 定义一个测试类(StudentDemo),在测试类中提供两个方法
一个方法是: useStudentBuilder(StudentBuilder s)
一个方法是主方法,在主方法中调用useStudentBuilder方法
Lambda表达式被引用类方法替代的时候,它的形式参数全部传递给构造器作为参数
函数式接口 1.函数式接口 1.1函数式接口概述函数式接口:有且仅有一个抽象方法的接口
Java中的函数式编程体现就是Lambda表达式,所以函数式接口就是可以适用于Lambda使用的接口只有确保接口中有且仅有一个抽象方法,Java中的Lambda才能顺利地进行推导
如何检测一个接口是不是函数式接口呢?
- @Functionallnterface放在接口定义的上方:如果接口是函数式接口,编译通过;如果不是,编译失败
注意
- 我们自己定义函数式接口的时候,@Functionallnterface是可选的,就算我不写这个注解,只要保证满足函数式接口定义的条件,也照样是函数式接口。但是,建议加上该注解
需求
-
定义一个类(RunnableDemo),在类中提供两个方法
一个方法是: startThread(Runnable r)方法参数Runnable是一个函数式接口一个方法是主方法,在主方法中调用startThread方法
如果方法的参数是一个函数式接口,我们可以使用Lambda表达式作为参数传递
startThread(0-> System.out.printin(Thread.currentThread0.getName()+ "线程启动了"));
需求
-
定义一个类(ComparatorDemo),在类中提供两个方法
一个方法是: Comparator getComparator()方法返回值Comparator是一个函数式接口一个方法是主方法,在主方法中调用getComparator方法
如果方法的返回值是一个函数式接口,我们可以使用Lambda表达式作为结果返回
private static Comparator
Java 8在java.util.function包下预定义了大量的函数式接口供我们使用
- Supplier接口
- Consumer接口
- Predicate接口
- Function接口
Supplier:包含一个无参的方法
- T get():获得结果
- 该方法不需要参数,它会按照某种实现逻辑(由Lambda表达式实现)返回一个数据
- Supplier接口也被称为生产型接口,如果我们指定了接口的泛型是什么类型,那么接口中的get方法就会生产什么类型的数据供我们使用
练习
- 定义一个类(SupplierTest),在类中提供两个方法
- 一个方法是: int getMax(Supplier sup)用于返回一个int数组中的最大值
- 一个方法是主方法,在伞方法中调用getMax方法
Consumer:包含两个方法
- void accept(T t):对给定的参数执行此操作
- default Consumer andThen(Consumer after):返回一个组合的Consumer,依次执行此操作,然后执行after操作
- Consumer 接口也被称为消费型接口,它消费的数据的数据类型由泛型指定
练习
- String[] strArray= {“林青霞30”,“张曼玉,35”,“王祖贤,33”};
- 字符串数组中有多条信息, 请按照格式:“姓名: XX年龄: XX”的格式将信息打印出来
- 要求:
把打印姓名的动作作为第一个Consumer接口的Lambda实例
把打印年龄的动作作为第二个Consumer接口的Lambda实例
将两个Consumer接口按照顺序组合到一起使用
Predicate:常用的四个方法
- boolean test(T t):对给定的参数进行判断(判断逻辑由Lambda表达式实现),返回一个布尔值
- default Predicate negate0:返回一个逻辑的否定,对应逻辑非
- default Predicate and(Predicate other):返回一个组合判断,对应短路与
- default Predicate or(Predicate other):返回一个组合判断,对应短路或
- Predicate 接口通常用于判断参数是否满足指定的条件
练习
- String[] strArray= {“林青霞30”,”柳岩,34",“张曼玉,35", “貂蝉,31”, “王祖贤,33”};
- 字符串数组中有多条信息, 请通过Predicate接口的拼装将符合要 求的字符串筛选到集合ArrayList中,并遍历ArrayList集合
- 同时满足如下要求: 姓名长度大于2;年龄大于33
Function
-
R apply(T t):将此函数应用于给定的参数
-
default Function andThen(Function after):返回一个组合函数,首先将该函数应用于输入,然后将after函数应用于结果
-
Function
接口通常用于对参数进行处理,转换(处理逻辑由Lambda表达式实现),然后返回一个新的值
练习
-
Strings = "林青霞30*;
-
请按照我指定的要求进行操作:
1:将字符串截取得到数字年龄部分
2:将上一步的年龄字符串转换成为int类型的数据
3:将上一步的int数据加70, 得到一个int结果 ,在控制台输出 -
请通过Function接口来实现函数拼接
需求:按照下面的要求完成集合的创建和遍历
- 创建一个集合, 存储多个字符串元素
- 把集合中所有以"张"开头的元索存储到一个新的集合
- 把"张"开头的集合中的长度为3的元素存储到一一个新的集合
- 遍历上一步得到的集合
使用Stream流的方式完成过滤操作
- list.stream().filter(s -> s.startsWith("张")).filter(s -> s.length() == 3).forEach(System.out::printIn);
- 直接阅读代码的字面意思即可完美展示无关逻辑方式的语义: 生成流、过滤姓张、过滤长度为3、逐一打印
- Stream流把真 正的函数式编程风格引入到Java中
Stream流的使用
- 生成流
通过数据源(集合,数组等)生成流
list.stream() - 中间操作
一个流后面可以跟随零个或多个中间操作,其目的主要是打开流,做出某种程度的数据过滤/映射,然后返回一个新的流,
交给下一个操作使用
filter() - 终结操作
一个流只能有一个终结操作, 当这个操作执行后,流就被使用“光” 了,无法再被操作。所以这必定是流的最后一个操作
forEach()
Stream流的常见生成方式
-
Collection体系的集合可以使用默认方法stream()生成流
default Stream stream()List
list = new ArrayList (); Stream listStream = list. stream(); Set set = new HashSet (); Stream setStream = set. stream(); -
Map体系的集合间接的生成流
Map
map = new HashMap (); Stream keyStream = map. keySet(). stream(); Stream valueStream = map. values(). stream(); Stream > entryStream = map . entrySet() . stream(); -
数组可以通过Stream接口的静态方法of(T… values)生成流
String[] strArray = {"he1lo", "world" , "java"}; StreamstrArrayStream = Stream . of(strArray); Stream strArrayStream2 = Stream. of("he11o","world", "java"); Stream intStream = Stream.of(10, 20, 30);
-
Stream filter(Predicate predicate):用于对流中的数据进行过滤
Predicate接口中的方法
boolean test(T t):对给定的参数进行判断,返回一个布尔值 -
Stream limit(long maxSize):返回此流中的元索组成的流,截取前指定参数个数的数据
-
Stream skip(long n):跳过指定参数个数的数据,返回由该流的剩余元索组成的流
-
static Stream concat(Stream a, Stream b):合并a和b两个流为一个流
-
Stream distinct():返回由该流的不同元素(根据Object.equals(Object) )组成的流
-
Stream sorted():返回由此流的元素组成的流,根据自然顺序排序
-
Stream sorted(Comparator comparator):返回由该流的元素组成的流,根据提供的Comparator进行排序
-
Stream map(Function mapper):返回由给定函数应用于此流的元素的结果组成的流
Function接口中的方法 R apply(T t) -
IntStream mapTolnt(ToIntFunction mapper):返回一个IntStream其中包含将给定函数应用于此流的元素的结果
IntStream:表示原始int流
ToIntFunction接口中的方法 int applyAsInt(T value)
Stream流的常见终结操作方法
- void forEach(Consumer action):对此流的每个元素执行操作
Consumer接口中的方法 void accept(T t):对给定的参数执行此操作 - long count(): 返回此流中的元索数
现在有两个ArrayLis集合,分别存储6名男演员名称和6名女演员名称,要求完成如下的操作
- 男演员只要名字为3个字的前三人
- 女演员只要姓林的, 并且不要第一个
- 把过滤后的男演员姓名和女演员姓名合并到一起
- 把上一 步操作后的元素作为构造方法的参数创建演员对象遍历数据
演员类Actor已经提供,里面有一个成员变量, 一个带参构造方法,以及成员变量对应的get/set方法
对数据使用Stream流的方式操作完毕后,我想把流中的数据收集到集合中,该怎么办呢?
Stream流的收集方法
-
R collect(Collector collector)
-
但是这个收集方法的参数是一 个Collector接口
工具类Collectors提供了具体的收集方式
-
public static Collector toList(): 把元索收集到List集合中
-
public static Collector toSet(): 把元索收集到Set集合中
-
public static Collector toMap(Function keyMapper,Function valueMapper):把元索收集到Map集合中
当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过类的加载,类的连接,类的初始化这三个步骤来对类进行初始化。如果不出现意外情况,JVM将会连续完成这三个步骤,所以有时也把这三个步骤统称为类加载或者类初始化
类的加载
- 就是指将class文件读入内存,并为之创建一个java.lang.Class对象
- 任何类被使用时,系统都会为之建立一个java.lang.Class对象
类的连接
- 验证阶段:用于检验被加载的类是否有正确的内部结构,并和其他类协调─致
- 准备阶段:负责为类的类变量分配内存,并设置默认初始化值
- 解析阶段:将类的二进制数据中的符号引用替换为直接引用
类的初始化
- 在该阶段,主要就是对类变量进行初始化
类的初始化步骤
- 假如类还未被加载和连接,则程序先加载并连接该类
- 假如该类的直接父类还未被初始化,则先初始化其直接父类
- 假如类中有初始化语句,则系统依次执行这些初始化语句
注意:在执行第2个步骤的时候,系统对直接父类的初始化步骤也遵循初始化步骤1-3
类的初始化时机:
- 创建类的实例
- 调用类的类方法
- 访问类或者接口的类变量,或者为该类变量赋值
- 使用反射方式来强制创建某个类或接口对应的java.lang.Class对象
- 初始化某个类的子类
- 直接使用java.exe命令来运行某个主类
类加载器的作用
- 负责将.class文件加载到内存中,并为之牛成对应的java.lang.Class对象
- 虽然我们不用过分关心类加载机制,但是了解这个机制我们就能更好的理解程序的运行
JVM的类加载机制
- 全盘负责:就是当一个类加载器负责加载某个Class时,该Class所依赖的和引用的其他Class也将由该类加载器负责载入,除非显示使用另外—个类加载器来载入
- 父类委托:就是当一个类加载器负责加载某个Class时,先让父类加载器试图加载该Class,只有在父类加载器无法加载该类时才尝试从自己的类路径中加载该类
- 缓存机制:保证所有加载过的Class都会被缓存,当程序需要使用某个Class对象时,类加载器先从缓存区中搜索该Class,只有当缓存区中不存在该Class对象时,系统才会读取该类对应的二进制数据,并将其转换成Cass对象,存储到缓存区
ClassLoader:是负责加载类的对象
Java运行时具有以下内置类加载器
- Bootstrap class loader:它是虚拟机的内置类加载器,通常表示为null,并且没有父null
- Platform class loader:平台类加载器可以看到所有平台类,平台类包括由平台类加载器或其祖先定义的Java SE平台API,其实现类和JDK特定的运行时类
- System class loader: 它也被称为应用程序类加载器,与平台类加载器不同。系统类加载器通常用于定义应用程序类路径,模块路径和JDK特定工具上的类
- 类加载器的继承关系: System的父加载器为Platform,而Platform的父加载器为Bootstrap
ClassLoader中的两个方法
- static ClassLoader getSystemClassLoader():返回用于委派的系统类加载器
- ClassLoader getParent():返回父类加载器进行委派
Java反射机制:是指在运行时去获取一个类的变量和方法信息。然后通过获取到的信息来创建对象,调用方法的一种机制。由于这种动态性,可以极大的增强程序的灵活性,程序不用在编译期就完成确定,在运行期仍然可以扩展
2.2获取Class类的对象我们要想通过反射去使用一个类,首先我们要获取到该类的字节码文件对象,也就是类型为Class类型的对象这里我们提供三种方式获取Class类型的对象
- 使用类的class属性来获取该类对应的Class对象。举例: Student.class将会返回Student类对应的Class对象
- 调用对象的getClass()方法,返回该对象所属类对应的Class对象
该方法是Object类中的方法,所有的Java对象都可以调用该方法 - 使用Class类中的静态方法forName(String className),该方法需要传入字符串参数,该字符串参数的值是某个类的全路径,也就是完整包名的路径
Class类中用于获取构造方法的方法
- Constructor> getConstructors():返回所有公共构造方法对象的数组
- Constructor> getDeclaredConstructors():返回所有构造方法对象的数组
- Constructor
getConstructor(Class>... parameterTypes):返回单个公共构造方法对象 - Constructor
getDeclaredConstructor(Class> ... parameterTypes):返回单个构造方法对象
Constructor类中用于创建对象的方法
- T newInstance(Object...initargs):根据指定的构造方法创建对象
练习1:通过反射实现如下操作
- Student s = new Student(“林青霞”,30,“西安”);
- System.out.println(s);
- 基本数据类型也可以通过.class得到对应的Class类型
Class> c = Class.forName("com.youquren.Student4");
Constructor> con = c.getConstructor(String.class, int.class, String.class);
Object o = con.newInstance("林青霞", 30, "西安");
System.out.println(o);
练习2:通过反射实现如下操作
-
Student s = new Student(“林青霞”);
-
system.out.println(s);
-
暴力反射
public void setAccessible(boolean flag):值为true,取消访问检查
Class> c = Class.forName("com.youquren.Student4");
Constructor> con =c.getDeclaredConstructor(String.class);
con.setAccessible(true);
Object o = con.newInstance("林青霞");
System.out.println(o);
2.5反射获取成员变量并使用
Class类中用于获取成员变量的方法
- Field[] getFields(:返回所有公共成员变呈对象的数组
- Field[] getDeclaredFields():返回所有成员变量对象的数组
- Field getField(String name):返回单个公共成员变量对象
- Field getDeclaredField(String name):返回单个成员变量对象
Field类中用于给成员变量赋值的方法
- void set(Object obj, Object value):给obj对象的成员变量赋值为value
练习:通过反射实现如下操作
- Student s = new Student();
Constructor> con = c.getConstructor(); Object o = con.newInstance();
- s.name = “林青霞”;
Field nameField = c.getDeclaredField("name");
nameField.setAccessible(true);
nameField.set(o,"林青霞");
System.out.println(o);
- s.age = 30;
Field ageField = c.getDeclaredField("age");
ageField.setAccessible(true);
ageField.set(o,30);
System.out.println(o);
- s.address =“西安”;
Field addressField = c.getDeclaredField("address");
addressField.setAccessible(true);
addressField.set(o,"西安");
System.out.println(o);
- System.out.println(s);
Class类中用于获取成员方法的方法
- Method[] getMethods():返回所有公共成员方法对象的数组,包括继承的
- Method[] getDeclaredMethods():返回所有成员方法对象的数组,不包括继承的
- Method getMethod(String name, Class>… parameterTypes)∶返回单个公共成员方法对象
- Method getDeclaredMethod(String name, Class>… parameterTypes):返回单个成员方法对象
Method类中用于调用成员方法的方法
- Object invoke(Object obj, Object…args):调用obj对象的成员方法,参数是args,返回值是Object类型
练习:通过反射实现如下操作
-
Student s = new Student();
-
s.method1();
Method m1 = c.getMethod("method1");
m1.invoke(o);
- s.method2(“林青霞”);
Method m2 = c.getMethod("method2", String.class);
m2.invoke(o,"林青霞");
- String ss = s.method3(“林青霞”,30);
Method m3 = c.getMethod("method3", String.class, int.class);
Object obj = m3.invoke(o, "林青霞", 30);
System.out.println(obj);
-
System.out.println(ss);
-
s.function();
Method m4 = c.getDeclaredMethod("function");
m4.setAccessible(true);
m4.invoke(o);
2.9反射练习
练习1:我有一个ArrayList集合,现在我想在这个集合中添加一个字符串数据,如何实现?
ArrayListarray=new ArrayList (); array.add(10); array.add(20); Class extends ArrayList> c = array.getClass(); Method m = c.getMethod("add", Object.class); m.invoke(array,"hello"); m.invoke(array,"world"); m.invoke(array,"java"); System.out.println(array);
练习2:通过配置文件运行类中的方法
//加载数据
Properties prop=new Properties();
FileReader fr = new FileReader("E:\IDEA\IdeaProjects\JavaSE_Code\idea_test\class.txt");
prop.load(fr);
fr.close();
String className = prop.getProperty("className");
String methodName = prop.getProperty("methodName");
//通过反射来使用
Class> c = Class.forName(className);
Constructor> con = c.getConstructor();
Object o = con.newInstance();
Method m = c.getMethod(methodName);
m.invoke(o);
模块化
1.模块化
1.1模块的基本使用
模块的基本使用步骤
- 创建模块(创建模块,创建包,创建类,定义方法)
为了体现模块的使用,我们创建2个模块。一个是myOne,一个是myTwo - 在模块的src目录下新建一个名为module-info.java的描述性文件,该文件专门定义模块名,访问权限,模块依赖等信息
描述性文件中使用模块导出和模块依赖来进行配置并使用 - 模块中所有未导出的包都是模块私有的,他们是不能在模块之外被访问的
在myOne这个模块下的描述性文件中配置模块导出
模块导出格式: exports包名; - 一个模块要访问其他的模块,必须明确指定依赖哪些模块,未明确指定依赖的模块不能访问
在myTwo这个模块下的描述性文件中配置模块依赖
模块依赖格式: requires模块名;
注意:写模块名报错,需要按下Alt+Enter提示,然后选择模块依赖
1.2模块服务的使用模块服务的使用步骤
- 在myOne模块下创建一个包ygj03,在该包下提供一个接口,接口中定义一个抽象方法
public interface MyService { void service(); } - 在ygj03包下创建一个包impl,在该包下提供接口的两个实现类ygj和wrx
- 在myOne这个模块下的描述性文件中添加如下配置
模块导出: exports ygj03;
服务提供: provides MyService with ygj;//指定MyService的服务实现类是ygj - 在myTwo这个模块下的描述性文件中添加如下配置
声明服务接口:uses MyService; - 在myTwo这个模块的类中使用MyService接口提供的服务
ServiceLoader:一种加载服务实现的工具



