编码格式
ISO-8859-1
网页常用 只支持英文
GBK
支持中文国标
类:class 外层框架
main
方法java
的主方法
package
Java的包
Interface
接口
打印
System.out.println("hello world");//输出并且换行显示
System.out.print("hello world");//输出不换行显示
//public 关键字 公用的
//static 静态的 静态方法和非静态方法no-static
//void 没有返回值
//main 入口函数(方法) 主函数唯一 程序执行起点
//() 函数参数 参数列表
//String 字符串
//[] 数组 多个值
//args 参数名称 argument缩写 s复数 多个值
//{}函数体 方法体 method body 代码写在方法体中
public static void main(String[] args){
/*
* System 系统提供api工具类
* . 点操作符
* out 系统输出设备(显示器);in 系统输入设备(键盘、鼠标)
* println 打印信息到控制台,ln=line换行
* ""括起来,定死,字符串
*/
System.out.prinln("Hello World");
}
测试代码错误
Junit
引入包
import org.junit.Test
在需要测试的方法上面写@Test
import org.junit.Test;
public class TestScores {
public static void main(String[] args) {
System.out.println("main");
}
//测试单元
@Test
public void print() {
// int i=5/0;
System.out.println("junit test");
}
}
变量赋值
int a = 66;
//先声明后赋值
int b;
b = 123;
//如果声明了 没有初始化 使用会报错
/*比如
int m;
System.out.println(m); 报错m还未赋值
*/
只能有数字,字母,下划线,美元符(_,$)开头,不能以数字开头
常量
在声明变量之前添加final
修饰符
final int PI = 3.14159;
//PI不能更改
基本数据类型
整数直接量可以直接赋值给byte,short, int long
但不能超出范围
byte short char
整型数据参与运算时 系统会一律转为int
再运算
byte t = 6;
byte e = 5;
byte f = t+e;//报错
byte f = (byte)(t+e);//正确
整型
byte
byte属于Java中的整型,长度为1字节8bit
2^8
short
short属于Java中的整型,长度为2字节16bit
2^16
短整型
int
int
是Java里常用的类型, 四个字节-2147483648 ~ 2147483648
2^32
两个整数相除, 结果还是整数 比如 5/2 是2
int
直接写出来的为整数直接量, 比如
int a = 5;//a就是整数直接量
不能超出存储范围, 超出编译错误
整数运算时超过整数自动溢出 比如
int a = 2417483647;
a = a+1 // -2417483647
long
长整型 范围非常大±9223372036854775808
2^64
long
的整数直接量在数字后面加上L或者l
(小写的L) 比如
long a = 10000000L;
运算时如果可能溢出建议第一个数字后面加L
比如
long num = 2000000000000L*2*10;//没问题
long sum = 1000000000*2*10L;//溢出 超过了int最大值
浮点类型
float
单精度浮点数,占4个字节,范围很大2^128
,为了与其他语言兼容
double
双倍精度浮点数, 占用8个字节,2^1024
浮点数直接量默认为double
类型,
若想表示 float
需在数字后加 F
或 f
double
和float
型数据参与运算时,有可能会发生舍入误差 比如
double a = 0.3;
double b = 0.1;
System.out.println(a-b)//0.19999999999998
字符型
char
0~65535
属于Java
的字符型, 占2字节16bit
可以赋值单个字符 比如
char a = 'a';
char b = '字';
char c = 12;//取值范围0~65536 对应ASCll字符编码中对应的字符
注意 char
只能存放单个字符 并且只能用单引号
特殊符号要用反斜杠\
进行转义
char a = '\'';
布尔型
boolean
仅有两个值true
false
数据类型转换
数据类型由小到大依次为
byte---short--int(char)--long--float--double
数据类型由小到大自动转换
int a = 5;
long b = a;
数据由大到小需要强制转换
long b = 5;
int a = (int)b;//(需要转换的类型)变量
强制转换有可能失去精度 和 数据丢失
double a = 22.22;
int b = (int)a;//丢失了小数点后面的数
byte short char
参与运算时底层逻辑先转换为int
再参与运算
包装类型
8个包装类型
byte Byte short Short int Integer long Long float Float double Double char Character boolean Boolean
Integer
字符串转整型/整数
parseInt()
传入一个字符串
必须是数字字符串
String s = "123";
int i = Integer.parseInt(s);// 转换整型
valueOf()
Integer.valueOf(127); //返回一个 Integer类型的整形
数据在-128 ~ 127
之间 用此方法创建具有高效的效果
自动装箱
编译器会自动把 基本类型 包装成 包装类型
交给包装类型的变量来保存 底层操作的就是valueOf()
自动 装箱 的 方向 : [基本类型] ==> [包装类型]
Integer num = 123;//自动装箱
自动拆箱
编译器会自动把包装类型拆掉"箱子" 变回基本类型
交给基本类型的变量来保存 底层操作就是 intValue()
自动 拆箱 的 方向 :[包装类型] ==> [基本类型]
Integer num = 213;
int n = num;//自动拆箱
运算符
+ - * / %
++a
和 a++
不同
先自增后赋值 先赋值后自增
int a = 10;
a++;//打印出来是10 a的值为11
int b = 10;
++b;//打印出来是11 a的值为11
关系运算符
>,<,>=,<=,==,!=
结果为布尔值
int a=5,b=6,c=7;
boolean b1 = a>b;//false
逻辑运算符
&& || !
与 或 非
- && 与 ,运算符两边都为
true
结果返回true
,只要有一个是false
结果就返回false
第一个为false
后面的不执行 - || 或 , 运算符两边都为
false
结果返回false
,只要有一个是true
结果就返回true
第一个返回true
后面的不执行 - ! 非 取反
赋值运算符
+= -= *= /= %=
int a = 10;
a += 5; //a的值为15 a=a+5
a *= 2; // a=a*2
字符串拼接运算符
System.out.println(1+1+1+"");// 3
System.out.println(1+1+1+""+1+1+1);// 3111
System.out.println(10+10+"10");// 2010
三目运算符
boolean表达式?表达式1 : 表达式2
如果为 true
返回表达式1 为false
返回表达式2
int a = 10;
System.out.println(a*10>10?"哈":"吼");//返回 哈
System.out.println(a>10?"哈":"吼");// 返回 吼
分支结构
基于条件来执行某语句,采用分支结构
if if...else if...else if switch...case
if(boolean){
//语句1
}else{
//语句2
}
多条结构
if....else if....
if(false){
//
}else if(false){
//
}else{
//
}
switch....case
进行比较的数值支持的类型 byte short char int String
执行顺序: 会拿着变量的值依次与每一个case后的值做比较
如果匹配, 就执行case后的语句
如果没有遇到break, 会一直向后执行所有case包括default, 称作: 穿透现象
如果设置类default"保底选项", 并且没有匹配到任何case, 执行default
break与default都是可选项, 加不加根据具体业务来决定
switch(数值){
case 1:
语句1
break;
case 2:
语句2
break;
default:
//前面都不执行最后执行这个
}
循环
当一个业务过程需要多次重复执行一个程序单元时候可以考虑使用循环流程控制实现
循环三要素
- 循环变量的初始化
- 循环的条件(以循环变量为基础)
- 循环变量的改变(向着循环的结束)
循环变量:在整个循环过程中反复改变的那个数字
while
while(boolean){
//语句块------
}
do....while
do{
//语句块
}while(boolean)
for
在循环当中应用最多的
for(要素1;要素2;要素3){
//语句 4
}
//顺序为1243243243243...
continue
跳过本次循环
break
跳出循环
高效循环
高效for
循环写法简单
效率高
for(1 2:3){代码块}
- 每轮遍历数据的类型
- 变量名
- 遍历的数据
Integer[] arr = {1,2,3};
for(Integer i:arr){
i //这个就是每次的数据
}
没有办法操作下标
引用类型
数组
数组是一种引用的数据类型, 数组是相同数据类型元素的集合
//声明整型数组arr 4个元素 每个元素都是int
int[] arr = new int[4]; //默认值都是0
//声明浮点类型数组d 包含20个元素 每个元素都是double
double[] douArr = new double[20];//默认值是0.0
//声明布尔类型数组 包含23个元素 每个元素都是boolean
boolean[] booArr = new boolean[23];//默认值都是false
数组的声明和初始化
int[] arr = new int[4];
int[] arr1 = {1,2,3,4,5};//声明并同时赋值 大括号
int[] arr2 = new int[]{1,2,3,4,5,6};//声明并同时赋值 大括号
//int[] arr3; arr={1,2} 错误的写法
int[] arr4;
arr4 = new int[]{1,2,3,4};
访问数组的长度
length
int[] arr = new int[20];
System.out.println(arr.length);//数组的长度
通过下标来访问数组中的元素 下标从0开始
int[] arr = new int[3];
arr[0] = 1;
arr[1] = 2;
arr[2] = 3;
//超出数组定义的长度会发生 数组下标越界异常
数组的遍历
利用for
循环达到遍历
int[] arr = new int[10];
for(int i=0;i<arr.length){
int j = arr[i];
}
数组的复制
System.arraycopy
int[] arr = {1,2,3,4};
int[] arr2 = new int[5];
System.arraycopy(arr,1,arr2,0,3)//arr数组的第1个下标开始复制到arr2从第0个下标开始复制3位
- 被复制的数组
- 被复制的数组下标
- 复制给的数组
- 复制给数组的开始下标
- 复制几位
数组的扩容和复制
Arrays.copyOf()
int[] a = {1,2,3,4,5};
int[] b = Arrays.copyOf(a,5);//1.被复制的数组 2.复制几位
//扩容
int[] a1 = {1,2};
a=Arrays.copyOf(a,a.length+1);
数组的排序
sort
一个参数 传要排序的数组
int[] arr = {4,2,5,3,1};
Arrays.sort(arr);//更改原数组
将对象转换为字符串
Arrays.toString
import java.util.Arrays;
Arrays.toString(arr);
队列
先进先出FIIFO
栈
后进先出LIFO
对象
包装类型都是对象object
基本类型,包装类型只能表达一个类型一个值
数组能表达多个值, 但是每个元素必须一致 只能一个类型
使用对象方法
类名 实例名 = new 类名();
实例化通过点操作符
person.name="张三";
person.age = 18;
person.sex = false;//男为true 女为false
成员变量推荐使用 包装类型
初始类型默认有初始值
//实体类 人 Person
public class Person {
//在类下面写的是成员变量
//在方法声明的变量是局部变量
//成员变脸使用包装类型来定义
public String name;//姓名
public Boolean sex;//性别
public Integer age;//年龄
public String address;//地址
}
使用并实例化
//不在同一个包路径下 需要导包
import cn.tedu.entity.Person;
public class TestPerson {
public static void main(String[] args) {
//创建对象实例
//属性初始化
//打印某个属性
Person p = new Person();
p.name = "狗蛋";
}
}
类
类是一类事物的抽象
类似于事物的模板
在一个Java文件中可以写多个class
但是被public
修饰的class
只能有一个
class Phon{}
创建对象时 内存发生什么事
- 在栈内存中开辟一块空间, 存放引用类型变量, 并把变量压入栈的最底部
- 在堆内存中开辟一块空间,存放设定的对象
- 完成对象的初始化, 并赋予默认值
- 给初始化完成的对象赋予唯一的地址值
- 把地址值交给定义的变量来保存
字符串
String
字符串底层维护的是 char[]
在堆内存中
String s = new String(new char[]{'a','b'})
String str = "张三";//写法简单 字符串存在堆内存中常量池里
toString()
转字符串 不推荐使用
哈希值
hashCode()
返回一个哈希值
String a = "123";
a.hashCode();
字符串连接符
concate()
一个参数 接受字符串
返回一个字符串
System.out.println("abc".concate("23"));// abc23
String a = "abc".concate("23");
字符串的长度
length()
String s = "abc";
System.out.println(s.length());// 3
获取字符串中某个字符
charAt()
一个参数 接受整型int
返回一个字符char
根据 下标 获取指定字符串的字符
String s = "abc";
System.out.println(s.charAt(0));// a
System.out.prinln(s.charAt(2));// c
去掉字符串两边的空格
trim()
String s = " qw e b ";
System.out.prinln( s.trim() );//qw e b
//去掉开头的空格 和末尾的空格
截取字符串
substring()
- 截取字符串的开始位置
- 截取字符串的结束位置
左闭右开(包含左不包含右)
如果只写一个参数
从当前位置开始截取到结束
String s = "张三丰";
System.out.println(s.substring(0,1));// 张
System.out.println(s.substring(1));// 三丰
获取字符所在的位置
indexOf()
传入需要获得位置的字符
获取某个字符 从左向右 首次 出现的位置
String songName = "周杰伦-双节棍.mp3";
System.out.println(".的位置 "+songName.indexOf(".")); //7
System.out.println("歌曲名称: "+songName.substring(0,songName.indexOf(".")));
System.out.println("扩展名: "+songName.substring(songName.indexOf(".")));
如果没有找到 返回-1
从右往左获取字符所在位置
lastIndexOf()
获取某个字符 从右向左 首次 出现的位置
String song = "周杰伦.双节棍.mp3";
System.out.println(song.lastIndexOf(".")); // 7
//歌曲名称
System.out.println("歌曲名称:"+song.substring(0,song.lastIndexOf("."))); // 歌曲名称:周杰伦.双节棍
全大写
toUpperCase()
String name = "HanMeiMei";
System.out.println(name.toUpperCase());
全小写
toLowerCase()
String name = "HanMeiMei";
System.out.println(name.toLowerCase());
后缀
endsWith()
返回一个布尔值
String name = "123.mp3";
System.out.println(name.endsWith(".mp3")); // true
前缀
startsWith()
String name = "123.mp3";
System.out.println(name.startsWith(".mp3"));// false
替换
replace()
-
被替换的字符串
-
替换的字符串
传不了正则
String name = "张三的歌曲非常好听 歌曲 张三";
System.out.println( name.replace("歌曲", "song") );// 张三的song非常好听 song 张三
replaceFirst()
只替换一个
从左往右数 满足的第一个
第一个参数可以传入正则
String name = "周杰伦.双节棍.mp3";
//将第一个任意字符替换成 -
System.out.println( name.replaceFirst(".", "-") );// -杰伦.双节棍.mp3
replaceAll()
替换所有
可以传 正则
String name2 = "周杰伦.双节棍.mp3";
//将所有的任意字符替换为 -
System.out.println( name.replaceAll(".", "-") );// -----------
字符串切分
split()
最终形成数组
以传入的字符串作为分隔 , 切分字符串
字符串当中但凡遇到 传入的字符串 都会切掉
String str = "语文,数学,英语,物理,化学";
String[] a = str.split(",");
System.out.println(Arrays.toString(a));// ["语文","数学","英语","物理","化学"]
特殊字符需要转义
整形转换字符串
valueOf()
整形int
转换成字符串
String.valueOf(10)+10;// 1010
字符串转换byte
数组
getBytes()
根据 ASCLL
表转换
String str = "abc";
byte[] b = str.getBytes();
StringBuffer
StringBuilder
String
的加强封装
拼接字符串
append()
StringBuffer sb = new StringBuffer();
sb.append("12");//拼接字符串12
超精度浮点数
BigDecimal
不要用double
类型作为参数 不然 不精确
BigDecimal d = new BigDecimal(3.14 + "")//最好传入字符串
加法 add()
BigDecimal bd4 = bd1.add(bd2);//要用 BigDecimal保存
减法 subtract()
乘法 multiply()
除法 divide()
除法有除不尽的情况发生 要设置保留位 和舍去的方法(四舍五入还是直接舍去)
- 指的是 除那个数字
- 保留位数
- 舍入方法
ROUND_HALF_UP
四舍五入
ROUND_HALF_DOWN
五舍六入
ROUND_HALF_EVEN
公平舍入 靠近哪个数就是哪个数
ROUND_UP
直接进位
ROUND_DOWN
直接舍弃
ROUND_CEILING
向上取整
ROUND_FLOOR
向下取整
BigDecimal bd4 = bd1.divide(bd2,3,BigDecimal.ROUND_HALF_UP);//除以 bd2 保留3位小数 四舍五入
随机数
Random
int a = new Random().nextInt();
double b = new Random().nextDouble();
接收一个参数
范围是[0,输入的数字)
int a = new Random().nextInt(10);//0-10之间的随机整数
时间
Date
GMT
格林尼治时间 UTC
协调世界时间
CST
中国标准时间
Date data = new Date();//返回当前时间
可以传入参数
long t = 1000;
new Date(t);//从 1970-1-1-00:00往后数1000
SimpleDateFormat
处理时间的工具类
Date date = new Date();//日期对象
SimpleDateFormat sdf = new SimpleDateFormat();//SimpleDateFormat对象
String s = sdf.format(date);//yy-MM-dd 上午(下午)HH-mm
自定义格式
SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月DD日 H时mm分ss秒");
String s = sdf.format(date);//2021年10月301日 16时33分44秒
获取当前时分秒
Calendar cal = Calendar.getInstance();
// 设置时间
cal.setTime(new Date());
System.out.println(cal.get(Calendar.MONTH) + 1);
System.out.println(cal.get(Calendar.DATE));
System.out.println(cal.get(Calendar.HOUR));
System.out.println(cal.get(Calendar.MINUTE));
System.out.println(cal.get(Calendar.SECOND));
获取时间戳
System.currentTimeMillis();
long t1 = System.currentTimeMillis();//1970年 1月 1日 开始的时间戳
Object
获取对象哈希码
hashCode()
Obj o = new Obj();
o.hashCode();//获取了哈希码值
返回一个 int
类型的数 对象的地址值
用来 区分 不同的 对象
toString()
底层的原理是获取 类名 + @
+ 十六进制的哈希码值
getClass().getName() + "@" + Integer.toHexString(hashCode());
子类想获取对象里的每个值需要 重写
@Override
public String toString() {//重写后打印 类型+所有属性和属性值
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", score=" + score +
", grade='" + grade + '\'' +
'}';
}
equals()
Object中的equals()的默认实现是 ==
比较 引用类型直接比较 地址值
如果想比较类里的属性需要 重写 equals()
和hashCode()
逻辑保持一致
@Override
public boolean equals(Object o) {//重写后 比较属性的每一个值
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return age == student.age &&
Double.compare(student.score, score) == 0 &&
Objects.equals(name, student.name) &&
Objects.equals(grade, student.grade);
}
@Override
public int hashCode() {
return Objects.hash(name, age, score, grade);
}
在String
中重写了这个方法 比较 的是两个字符串的 具体内容
String
也重写了hashCode
方法根据字符串内容生成哈希码值
局部变量和成员变量
局部变量
在方法里 或 局部代码块中
必须 手动初始化 --给变量赋值
在方法里 或 局部代码块里 对应的代码执行完毕, 局部变量也随之 释放
成员变量
类里方法外
无需 手动初始化,会赋予对应类型成员变量的默认值
在整个类中都生效, 类消失, 成员变量才会消失
方法
方法也称为函数,用于封装一段特定的业务逻辑功能 一个方法只做一件事 并且方法可以被调用多次
方法的定义
修饰词 返回值类型 方法名称(参数列表){
//代码
方法体
}
//公开的 静态的 没有返回值的 speak方法
public static void speak(){
//
}
通过 方法名+参数列表 的方式来确定具体调用那个方法的功能
重载
在同一个类中,存在多个方法名相同,但是参数列表不同的方法 就是重载
public static void method() {
System.out.println("哈哈哈我没有参数");
}
public static void method(int a) {
System.out.println("哈哈哈我的参数是:"+a);
}
在同类中, 同名方法的参数个数不同, 一定构成重载
在同类中, 同名方法的参数个数相同, 参数类型不同, 一定构成重载
方法重载条件:
- 在同一个类中
- 多个方法的方法名相同
- 同名方法的参数列表不同
- 如果参数个数不同, 一定构成重载
- 如果参数个数相同, 查看对应位置上的参数类型, 与参数名无关
面向对象
面向对象是一个编程思想:
三大特征:
封装, 继承, 多态
封装
用private
修饰资源
提供公共的get
方法和set
方法
封装方法的思路
用private
修饰资源
用本类的公共方法调用这个被封装的方法的功能
用来保护私有的方法
封装实体:
- 私有属性
private
- 对应每个私有属性,有两个方法
getName()
获取setName()
设置
私有属性无法直接进行访问 必须通过get
和set
进行访问
public class Person {//私有
private String name; //姓名
private Double money; //薪水
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
使用
import cn.tedu.entity.Person;
public class TestPerson {
public void person() {
//类名 名称 = new 类名()
Person person = new Person();
person.setName("陈晨");//set设置
person.setMoney(10000D);
System.out.println(person.getName());//get获取
System.out.println(person.getMoney());
}
}
@Override
注解 , 重写, 重构,覆盖 父类的方法
构造函数
constructor
每次new
对象时都会触发对应类中的构造方法
这个构造方法是没有返回值的
每一个类中都会默认存在一个没有参数的构造方法
构造方法的格式与本类的类名一致 并且没有返回值类型
class Person{
String name;
public Person(){
//这是构造函数
}
public Person(int a){
//这是有参构造函数
}
}
如果不写无参数的构造函数会被自己的有参构造函数覆盖
从而生成不了没有参数的构造方法
构造函数里面可以执行一些赋值操作
无参构造: 默认存在, 添加了其他构造 默认的构造函数会覆盖
含参构造: 对于参数没有任何要求, 有参数就行
全参构造: 全参的构造函数参数必须与本类属性一致
构造代码块
构造代码块就是一个大括号{}
位置在 类里方法外
并且每次new
一个类时都会执行构造代码块
构造代码块 优先与构造函数 执行
class Pig{
int a = 0;
{
//这是构造代码块
}
}
局部代码块
局部代码块是一个大括号{}
位置在 方法里
调用本局部代码块所在的方法时才会
this
this
指的就是当前所在的类的地址
执行this()
必须写在 构造函数 的第一行
继承
extends
继承是面向对象最显著的一个特征
从已有的类派生出新的类, 新的类能吸收已有类的数据属性和行为, 并扩展新的能力
Java
继承会用已存在的类定义作为基础建立新类的技术
新类的定义可以增加新的数据或新的功能,也可以使用父类的功能,但不能选择性的继承父类
写法格式如下
子类 extends 父类{}
单继承 多继承 不同类继承同一个类
Java
不支持多继承
继承extends
只能单继承
接口可以多个接口
public void goudan extends dachui(){}//继承dachi类
继承的好处 减少相同的代码
一个子类只能有一个父类
一个父类可以有多个子类
继承具有传递性
父类的功能会传给子类
子类继承了父类以后 可以使用父类所有 不是私有 的资源
私有资源被private
修饰没有访问权限
继承要求子类必须是父类的一种下属类型, 依赖性非常强
子类访问父类变量
super
当父类的成员变量与子类的成员变量同名时, 可以使用super
指定父类的成员变量
super
可以看做是父类的对象
super.name//调用父类的成员变量name
当父类的成员变量与子类的成员变量重名时
使用super
指定父类的成员变量
子类在创建对象时 会默认调用 父类 的构造方法
因为子类的构造函数里第一行 默认存在 super()
调用父类的无参构造
当父类 没有 无参构造时必须通过super(参数)
调用父类的其他含参构造
子类 必须调用 一个父类的构造函数 不管是无参还是有参
Interface
接口 创建
制定 规则 和一套 标准
接口中的方法都是 抽象方法
定义接口 使用 Interface
实现接口 使用 implements
接口只定规矩 不实现
继承接口时 可以 实现继承接口内 定义的方法
public interface Animal {
public void eat();//只定义方法,不实现 只定义一个框架,不填充内容
}
使用
public class Rabbit implements Animal{//继承接口
@Override
public void eat() {
System.out.println("兔子吃草草");
}
}
也 可以不实现 接口内的方法 但是必须 把自己 变成抽象类
public interface Animal {
public void eat();//只定义方法,不实现 只定义一个框架,不填充内容
}
abstract public class Rabbit implements Animal{//继承接口 不实现方法 但是自己 必须是 抽象的类
//
}
接口里面 没有构造方法 没有构造代码块
如果一个类 没有指定父类 默认继承 顶级父类 object
如果一个类 没有继承父类 在构造方法里执行 super()
默认执行顶级父类 object
构造方法
在接口里定义的 成员变量 不是成员变量 是 静态常量 public static final
用 接口名 去调用
不能进行修改
interface UserInter{
int age = 3;//实际上是 public static final int age = 3 可以简写为 int age = 3;
}
当然 接口里定义的 抽象方法 也可以 简写 void demo();
接口的继承
子接口可以 继承多个 父接口
interface Inter extends demo1,demo2{}// 接口同时 继承 多个接口
实现类可以 实现多个 接口
class InterImpl implements demo1,demo2{}//类同时 实现 多个类
java
类 实施: 单继承 多实现 规则
java
接口 实施: 单继承 多继承 规则
重写
重写的原则
两同 两小 一大
两同: 子类方法的 方法名与参数列表 和父类方法的参数列表相同
一大: 子类方法的 方法修饰符权限 大于等于 父类方法的修饰符
两小: 子类方法的返回值类型 小于等于 父类方法的返回值类型 (指的是返回值类型是父类返回值类型的子类 要有继承关系)
子类抛出异常小于等于父类方法抛出异常
@Override //如果这个注解不报错就是 重写方法 重写父类的方法
public void eat(){}
默认方法
在返回值前加上参数default
默认方法可以不被类实现
default void play(){
System.out.println("这里可以有方法体");
}
接口中的静态方法
接口中可以定义静态方法
必须有 方法体
只能 用 接口名 去调用
static void play(){
//可以有方法体
}
只能用 接口名 去调用 不能用实现类 去调用
多态
多态的前提是 要有 继承关系 要有 方法的重写
父类引用指向子类对象
父类 变量 = new 子类();
创建出来的子类对象的 地址值 交给父类类型的引用类型变量保存
比如: 儿子赚的钱可以交给父亲保存 父亲赚的钱不能给儿子保存
编译看左边 运行看右边
必须要在父类定义这个方法 才能通过编译 把多态对象 看作是父类 类型
必须要在子类重写这个方法 才能满足多态
多态中成员变量使用的是 父类的成员变量
多态中方法的声明使用是父类 方法体使用的是子类的 重构方法
同一个接口 最终结果是不同的
调用同一个方法 出现不同的结果
//定义接口
public interface Animal(){//animal 接口里必须写eat方法
public void eat();
}
//定义类 使用接口
public class Cat implements Animal(){
public void eat(){
//执行代码
}
}
//使用
public class Per{
public void eat(Animal animal){//传入接口 只要继承接口的类都会执行这个方法
animal.eat();
}
}
静态static
被static
修饰的资源统称为 静态资源
静态资源随着类加载而加载到内存中的 比对象 优先 进入内存
静态资源可以通过 类名直接调用 及时没有创建对象也可以调用
静态资源 优先与对象 进行加载
静态资源在内存中只有一份 被全局所有对象共享
只要通过任意一种方式进行修改 到处都修改
Demo.a//通过类名直接调用
Demo w = new Demo();
Demo w2 = new Demo();
Demo.a = 22;
w2.a//一处修改 到处修改 22
class Demo{
public static int a;
}
在静态方法里 不能使用 非静态的资源
而非静态资源 可以使用 静态的资源
class Demo{
static int a;
int b;
public static void demo(){
a//可以调用
b//不能调用
}
}
静态资源内不允许使用this
和super
关键字
静态代码块
与构造代码块位置一致 类里方法外
class Demo{
static{
//静态代码块
}
}
静态代码块只会 加载一次
final
被final
修饰的类 不能被继承 为最终类
final class Demo{}//这个类不能被继承
被final
修饰的类 不能被重写 为最终方法
final public void demo(){}//这个方法不能被子类 重写
被 final
修饰的字段是常量 不能被修改
常量的名称必须大写 单词与单词之间使用 _
分割
异常
ArithmeticException
算数异常
InputMismatchException
输入异常
ArrayIndexOutOfBoundsException
数组下标越界异常
异常捕获
try
catch
try{
//可能会抛出异常的代码
}catch(异常的类型 异常的名字){
//捕获到异常进行处理的解决方案
}
异常抛出
throws
异常抛出的格式: 方法的小括号与大括号之间写 throws 异常类型
有多个异常 使用逗号链接
private static void method() throws Exception {}
如果一个方法抛出了异常 那么 谁调用 的方法 谁就处理 这个异常
要么 捕获异常 要么 向上抛出
但是会在main
主函数调用之前解决掉异常
主函数main
不直接调用会抛出异常的方法
而是用过中间方法调用会抛出的异常的方法
finally
不论是否捕获到异常 都一定会执行finally
里面的内容
try{}catch(Exception e){}finally{
//
}
抽象
abstract
被 abstract
修饰的方法是抽象方法 **没有方法体 **
public abstract void eat();
如果一个类中包含了抽象方法 这个类 必须 被声明成一个 抽象类
abstract class Demo{//抽象类
public abstract void play();
}
如果一个类 继承了 抽象类 这个类
要么 实现 抽象类里面的 抽象方法
要么 变成抽象类
抽象类是 不能 被 创建成对象
抽象类是为了 子类创建对象 时使用 即:super()
抽象类里可以有 构造方法 构造代码块
内部类
即 在类里 定义的类称为 内部类
创建 内部类方式
// 外部类名.内部类名 对象名 = 外部类对象.内部类对象
Demo.Inter i = new Demo().new Inter();
new Demo().new Inner();//匿名的Demo里的内部类
-
内部类可以 直接访问 外部类中的资源 包括私有的资源
privet
-
外部类访问内部类资源 必须 创建内部类对象 通过对象调用
class Demo{ public void test(){ int n = new Inner().sum;//使用对象调用内部类属性 } class Inner{ int sum; } }
-
在成员位置的内部类是 成员内部类
-
在局部位置的内部类是 局部内部类
private
修饰
被 private
修饰的私有资源类 不能 直接创建对象
可以通过 封装 的方法调用
static
修饰
被 static
修饰 优先与对象 进行加载
直接通过外部类的类名进行加载
new Demo.Inner();// new 外部类名.内部类();
访问静态类的静态方法 不用创建对象 通过类名创建
Demo.Inner.methods();// 外部类名.内部类名.方法名();
局部内部类
在方法里定义的类是 局部内部类
class Demo{
public void demo(){
class Inner{
//局部内部类
}
}
}
要执行局部内部类的功能 必须创建 局部内部类对象 并调用
注意 执行性死区
先定义局部内部类
再创建 局部内部类的对象
其他用法
new Demo(){
public void demo(){}
}.demo();//匿名内部类 和匿名对象
class Demo{
//代码
}
IO
流
即in
和out
流只能单方向流动
输入流进行 读取 => in
输出流进行 写出 => out
在 Java
根据处理的数据单元不同把流分为 字节流 和 字符流
字节流: 针对二进制文件 (图片 视频)
字符流: 针对文本文件 (文字文件)
字节输入流: InputStream
是个抽象类
字节输入流 FileInputStream
会抛出异常 需要用 try...catch...
读取一个字节 read()
每次 调用 会读取一个字节
返回 int
类型的数据
如果没有内容 返回 -1
InputStream in = new FileInputStream("D:\\ready\\1.txt");
in.read();// 读取第一个字节
in.read();// 读取第二个字节
例如
try {
InputStream in = new FileInputStream("D:\\ready\\1.txt");
int b = 0;
while ((b = in.read()) != -1){
System.out.print( b+"" + "");
}
} catch (Exception e) {
e.printStackTrace();
}
文件流打开之后需要进行 关闭 流
关闭流 close()
关流操作一般写在try..catch..
的最后面 finally
里面
因为对文件进行操作时可能导致错误
从而文件没有执行 关闭流
try{}catch(Exception e){
//
}finally{
file.close();
}
高效字节输入流 BufferedInputStream
不能直接传入路径
需要传入 InputStream
对象
FileInputStream
继承InputStream
对象
InputStream in = new BufferedInputStream(new FileInputStream("D:\\ready\\1.txt"));
字节输出流: OutputStream
是个抽象类
在文件里写入字节
字节流输出FileOutputStream
传入文件的目录地址
new FileOutputStream("D:\\ready\\1.txt");
可以进行 追加 内容
第二个参数 boolean
new FileOutputStream("D:\\ready\\1.txt",true);//追加
写入 write
out.write(97);// 在文件里写入了 97对应的a
高效字节流输出 BufferedOutputStream
new BufferedOutputStream(new FileOutputStream("D:\\ready\\1.txt"));//高效字节流输出
字符输入流: Reader
用来处理纯文本数据
字符输入流 FileReader
和字节输入流相同
new FileReader("D:\\ready\\1.txt");
read()
close()
高效字符输入流 BufferedReader
和 高效字节输入流相同
new BufferedReader(new FileReader("D:\\ready\\1.txt"));
字符输出流: Writer
输出的是 字符
字符流输出FileWriter
new FileWriter("D:\\ready\\1.txt");
new FileWriter("D:\\ready\\1.txt",true);//追加内容
高效字符输出流BufferedWriter
new BufferedWriter(new FileWriter("D:\\ready\\1.txt"));
注意 如果创建了多个流那么
最后创建 的 最先关闭
编码转换流
针对文本文件,读写容易出现乱码的现象,在读写时,最好指定编码集为UTF-8
OutputStreamWriter
OutputStreamWriter(OutputStream)
把传入的字节流转成字符流
OutputStreamWriter(OutputStream,String)
指定unicode
编码进行输出
InputStreamReader
InputStreamReader(InputStream)
把传入的字节流转成字符流
InputStreamReader(InputStream,String)
读取其他编码转成Unicode
new InputStreamReader(new FileInputStream("1.txt"),"utf-8"));
文件类 File
File f = new File("文件目录");
文件字节大小
length()
f.length();//字节大小
判断是否是文件夹
f.isDirectory()
f.isDirectory()//true / false 是否是文件夹
判断是否是文件
f.isFile()
f.isFile()// true / false 是否是文件
获取文件名
f.getName()
f.getName()// 文件本身的名称
获取父级目录
f.getParent()
f.getParent()// 获取父级路径
获取文件绝对路径
f.getAbsolutePath()
f.getAbsolutePath()// 获取绝对路径
当前文件是否存在
f.exists()
f.exists()// 判断当前 路径 / 文件 是否存在 true or false
创建文件
f.createNewFile()
需要处理 方法抛出的异常
方法返回 boolean
没有当前文件 创建成功返回true
存在当前文件 创建失败返回 false
f.createNewFile()// 根据路径创建文件
创建文件夹
f.mkdir()
f.mkdir();// 根据路径创建 文件夹 true false
f.mkdirs()
File f = new File("D:\\ready\\a\\b\\c");
f.mkdirs();// 创建多层 文件夹
删除文件夹 / 文件
f.delete()
只能删除内容为空的 文件夹
f.delete();// true / false 删除失败返回false
展示文件
f.list()
String[] list = f.list();//存储的是文件的名称
System.out.println(Arrays.toString(list));//文件的名称
f.listFiles()
File[] fs = f.listFiles();//存储的是 文件的路径 可以进行后续操作
System.out.println(Arrays.toString(fs));
序列化与反序列化
序列化指的是将 对象的状态信息 转换为 可以 存储 或 传输 形式的过程
序列化 : ObjectOutputStream
把Java
对象保存到磁盘存储空间中
反序列化: ObjectInputStream
把序列化的数据读取出来
如果对象想要被序列化输出 必须实现 Serializable
接口
作用是 作为标记 标记这个类的对象可以被序列化
class Student implements Serializable{}
writeObject
写入对象
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("D:\\ready\\1.txt"));
Student obj = new Student();
out.writeObject(obj);//写入对象
序列化出来的内容需要通过 反序列化 回复成对象才能使用
readObject
读取对象
ObjectInputStream in = new ObjectInputStream(new FileInputStream("D:\\ready\\1.txt"));
in.readObject();//读取对象
一次序列化 对应 一次反序列化
因为 JVM
会给每个要需要序列化输出的类 分配 一个随机的serialVersionUID
只要 更改 了这个类自动生成的UID
会进行更改
当然 serialVersionUID
可以自行设置
需要在输出的类里设置
class Student{
public static final long serialVersionUID = 123L;//名字必须是 serialVersionUID
}
泛型
大概范围的类型
或 规定范围的类型
叫做 泛型
List<String> list = new ArrayList();//约束了数据 只能存储 字符串 类型的 集合
模拟了数组的 约束元素类型
泛型 通常与集合一起使用 用来约束集合中元素的类型
可以 约束 数据的 类型 把报错时机提前
不能约束 八大基本类型
只能约束 包装类型 或 引用类型
泛型可以实现通用代码的编写
使用E表示元素类型的Element
元素
在方法上使用泛型需要:
- 传入参数的类型为泛型
- 返回值前添加泛型类型
public static <E> void demo(E[] e){}//泛型 的数组
集合
Collection
add
返回布尔值 更改原集合
addAll
返回布尔值 更改原集合
clear
没有返回值
hashCode
返回整数
toArray
返回对象类型的数组
contains
返回布尔值
containsAll
返回布尔值
isEmpty
返回布尔值
remove
返回布尔值 更改原集合
removeAll
返回布尔值 更改原集合
size
返回整数
retainAll
返回布尔值 更改原集合
接口
集合是用来存放对象的数据结构
长度可变
可以存放 不同类型 的对象
Collection<Integer> c = new ArrayList();
因为Collection
是抽象类 不能进行实例化 需要用
实现子类进行创建
List
接口
数据是
有下标
有序
可重复 的
add(int,String)
根据下标添加 没有返回值
indexOf(String)
获取第一次出现的下标 返回整数
lastIndexOf(String)
获取最后一次出现的下标 返回整数
remove(int)
根据下标删除元素 返回删除的元素
get(int)
获取下标元素的值 返回获取的元素
set(int,String)
根据下标修改元素 返回被删除的元素
addAll(int,List)
根据指定的下标添加集合元素 返回布尔值
ArrayList
内部是用数组结构存放数据
内部初始容量是10 如果不够会以1.5倍增长
查询块 增删效率低
类
添加元素add()
Collection<Integer> c = new ArrayList<>();
c.add(1);
清空全部元素clear()
c.clear();
获取哈希码值hashCode()
c.hashCode();
是否包含某个元素contains()
c.contains(100);//是否包含指定的元素 100 返回布尔值
集合是否为空isEmpty()
c.isEmpty();//判断c集合是否为空 返回布尔值
删除指定元素remove()
c.remove(100);//删除元素中 指定元素 100 返回布尔值
获取集合中元素的个数size()
c.size();//返回集合的长度
转为数组toArray()
因为不清楚存储的数据类型
所以返回Object
类型的数组
Object[] arr = c.toArray();//转换为Object类型的数组
合并两个数组 addAll()
会更改复制的数组
c.addAll(c2);//向集合的后面添加上 c2的集合 更改了 c集合 返回布尔值
属于另一个集合的所有元素删除 removeAll()
只要本集合里的元素 有一个满足 另一个集合里的一个元素
就删除
Collection<Integer> c = new ArrayList<>();
c.add(100);
c.add(200);
Collection<Integer> c2 = new ArrayList<>();
c2.add(200);
c2.add(300);
c.removeAll(c2);//返回 布尔值
//c集合只剩下 [100]
是否包含某个集合containsAll()
c.containsAll();//返回 布尔值
只保留与另一个集合的公共元素retainAll()
Collection<Integer> c = new ArrayList<>();
c.add(100);
c.add(200);
Collection<Integer> c2 = new ArrayList<>();
c2.add(200);
c2.add(300);
c.retainAll(c2);//返回 布尔值
//最终 c 集合里只剩下 [200]
LinkedList
底层用链表实现 (相当于链条)
查询慢 增删块
靠着节点之间的地址值联系起来
remove()
不加参数默认删除第一个元素 返回删除的元素
removeFirst()
删除第一个元素 返回删除元素
removeLase
删除最后一个元素 返回删除元素
addFirst(o)
添加首元素 添加第一个元素 没有返回值
addLast(o)
添加尾元素 添加最后一个元素 没有返回值
getFirst()
获取首元素 获取第一个元素 返回第一个元素
getLast()
获取尾元素 获取最后一个元素 返回最后一个元素
element()
查看首元素 返回第一个元素
peek()
查看首元素 返回第一个元素
peekFirst()
查看首元素 返回第一个元素
peekLast()
查看尾元素 返回最后一个元素
offer()
添加尾元素 返回布尔值
offerFirst()
添加首元素 返回布尔值
offerLase()
添加尾元素 返回布尔值
poll()
删除首元素 返回删除元素
pollFirst()
删除首元素 返回删除元素
pollLast()
删除尾元素 返回删除元素
Set
接口
数据是
无下标
无序
不可重复
HashSet
Map
接口
数据是
键值对
形式进行存储
具有对应关系
存放的数据是无序的
根据键获取值
键 不允许重复 ,如果 重复 对应的值会 覆盖
Map<Integer,String> map = new HashMap<>();//存入一对数据 定义两个
put()
通过键值对添加数据
clear()
清空集合 没有返回值
isEmpty()
是否为空 返回布尔值
size()
获取键值对个数 返回整数
containsKey()
是否包含指定的key
返回布尔值
containsValue()
是否包含指定的Value
值 返回布尔值
values()
查看所有的value
值存入集合中 返回Collection
类型的集合
get()
根据Key
值获取 Values
的值 返回Value
HashMap
HashMap
的结构是数组+链表 或者 数组+红黑树 的形式
存放的是Entry
数组
Entry
里面是一对的键值对
底层原理 : Entry
数组默认长度16
存入数据是对Key
键 取hash
值并对数组长度取余 存入
如果重复(余数相同)将在已经存入的数据后面用 链表 形式挂起
如果挂起数量超过 8 个转为红黑树 小于6个转为链表
加载因子 0.75f
集合的迭代
Iterator
hasNext()
是否有下一个元素
next()
获取当前元素
//集合的迭代
//获取集合的迭代器
//判断集合中是否有下一个元素可以迭代
//获取当前迭代到的元素
Iterator<Integer> it = c2.iterator();//获取迭代器
while (it.hasNext()){
System.out.println(it.next());
}
ListIterator
list
接口 独有 的迭代器
hasPrevious()
是否有上一个元素
previous()
获取上一个元素
Map
的迭代问题
map
本身没有迭代器需要将自己转为 Set
用Set
的迭代器
keySet
方法
Set<Integer> set = map.keySet();//转换 set
Iterator<Integer> it = set.iterator();//获取迭代器
while (it.hasNext()){
System.out.println(map.get(it.next()));//获取
}
entrySet
方法
把一对键值对看成一个Entry
Map.Entry<Key,Value> = map.entrySet();//Key是key的类型 Value是value的类型
Set<Map.Entry<Integer, String>> set1 = map.entrySet();//entry存储的是 一个键 一个值
Iterator<Map.Entry<Integer, String>> it3 = set1.iterator();
while (it3.hasNext()){
Map.Entry<Integer, String> next = it3.next();
Integer key = next.getKey();//获取Key
String value = next.getValue();//获取/value
System.out.println("{"+key+":"+value+"}");
}
多线程
如果有一个线程: 单线程
如果有多个线程: 多线程
线程的 三态 模型
就绪 执行 阻塞
执行 ==> 阻塞 ==> 就绪 ==> 执行/终止
Thread.sleep(long)
休眠
方法1
继承Thread
类
在主方法里用start()
以多线程启动 调用类里面的run()
方法
class Demo extends Thread{
public void run() {
//业务逻辑
}
}
//用多线程启动
new Demo().start();//底层逻辑是会调用 类里面的run方法
getName()
线程名称 (父类方法)
方法2
实现Runnable
接口
添加run
函数 里面写业务代码
class Demo implements Runnable{
public void run(){}
}
启动
new Thread(new Demo()).start();//启动多线程
Thread.currentThread().getName()
获取当前线程名称
线程是会有 安全问题
由于线程的 随机性 和 访问延迟
一下情况会出现 安全问题
- 多线程程序
- 有共享数据
- 操作共享数据
同步锁
解决多线程的安全问题
同步:
同一时刻只能有一个线程独占资源
异步:
多线程抢占资源 线程间互相不等待 互相抢占资源
同步锁 相当于给容易出问题的代码加了一把锁
包裹住可能会出现数据安全问题的代码
但是锁的范围需要考虑
不能太大效率低
不能太小锁不住会有安全隐患
synchronized(锁对象){会出现安全隐患的代码}
在同步代码块中的代码
同一时刻只会被一个线程执行
同步锁中的锁对象 必须唯一
类型不做限制
可以用当前类名的class
当前类名.class
线程池
Executors
是用来辅助创建线程池的工具类
Executors.newFixedTHreadPool(int) //这个方法可以创建指定数目线程的线程池对象
通过这个方法创建出来的对象负责进行 线程的操作
execute()
方法 让线程池中的线程执行业务
每次调用都会将线程加入队列中
ExecutorService pool = Executors.newFixedThreadPool(5);
pool.execute(target);//target 是执行的业务 对象
注解
用来自动生成代码 效率更高
注解分为三大类:
JDK
自带注解
元注解
自定义注解
@Override
用来表示重写方法
元注解: 用来定义其他注解
@Target
注解用在哪里:类上, 方法上, 属性上等@Retention
注解的生命周期: 源文件中, 字节码文件中, 运行中允许子注解继承@Inherited
生成javadoc时会包含注解,不常用@Documented
注解为可重复类型注解,可以在同一个地方多次使用,不常用@Repeatable
自定义注解
注意 :注解定义的语法和Java
不一样
格式如下
@interface 注解名{}
通过 @Target
注解标记自定义注解可以使用的位置
@Target
需要一个ElementType
类型的数组,用来存放使用的位置
@Target({ElementType.TYPE,ElementType.METHOD})//TYPE类里 METHOD方法里
@interface Demo{}
或者
@Target(ElementType.TYPE)
@interface Demo{}
通过@Retention
注解标记 自定义注解的生命周期
里面的值 只能有一个
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)//到运行时都有效
@interface Demo{}
注解内部可以定义属性
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface Demo{
int age() default 0;//代表默认值是0的 age
}
如果不写默认值 使用注解必须赋值
@Demo(age=10)
可以定义特殊属性
名称必须是value
类型不做限制
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)//到运行时都有效
@interface Demo{
String value();
}
之所以特殊因为 使用注解 进行 单个 value
赋值可以不写 属性名
@Demo("狗蛋")
反射
可以操作 类里面所有的信息
包括 私有属性 构造方法
先获取字节码对象
1. 类名.class
2. Class.forName("目标类全路径")
3. 目标类对象.getClass()
class Demo{}
//用类名 获取字节码对象
Demo.class;
//用Class获取字节码对象
Class.forName("cn.Demo");
//用目标类对象 获取字节码对象
new Demo().getClass();
获取全部路径的类名getName()
返回String
类型
获取包对象getPackage()
返回Package
包对象
获取包对象的名字getPackage().getName()
返回String
类型
获取类名getSimpleName()
返回 String
类型的当前类名
获取所有公开的成员变量(包括继承的)getFields()
返回一个Field
类型的数组
Field[] fs = aClass.getFields();
/* 必须是公开的属性才能获取到*/
for (Field f : fs){
System.out.println(f.getName());//获取属性的名称
System.out.println(f.getType());//获取属性的类型
}
获取本类的所有成员变量包括私有(不包括继承) getDeclaredFields()
返回一个Field
类型的数组
获取所有公开的方法(包括继承的)getMethods()
返回一个Method
类型的数组
Class<Student> aClass = Student.class;
Method[] methods = aClass.getMethods();
for (Method m : methods){
System.out.println(m.getName());//获取方法的名字
System.out.println(Arrays.toString(m.getParameterTypes()));//方法的参数列表 返回包对象的数组
}
获取所有的方法(不包括继承,包括私有)getDeclaredMethods()
返回一个Method
类型的数组
获取本类所有公开的构造方法getConstructor()
返回Constructor
类型的数组
Class<?> aClass = new Student().getClass();
Constructor<?>[] cs = aClass.getConstructors();
for (Constructor<?> con : cs){
System.out.println(con.getName());
System.out.println("构造方法的参数类型"+Arrays.toString(con.getParameterTypes()));
}
获取本类所有的构造方法(包括私有)getDeclaredConstructors()
返回Constructor
类型的数组
利用反射创建对象newInstance()
返回创建的无参构造对象
利用反射创建含参构造
Constructor<?> demo = aClass.getConstructor(String.class, int.class);//传入类型的 字节码对象
Object o = demo.newInstance("李狗蛋", 12);
System.out.println(o);
暴力反射
原理是先
获取设置的属性
设置资源的可见权限
设置给哪个对象 什么值
Field name = aClass.getDeclaredField("name");//获取 name 属性
Object obj = aClass.newInstance();//创建一个无参构造对象
name.setAccessible(true);//将私有属性 设置成资源的可见性
name.set(obj,"狗蛋");//属性 name 设置给 obj 对象 值为 狗蛋
获取私有方法
getDeclaredMethod(m,x,y,z...)
m
要获取方法的名字
x,y,z...
可变参数 目标方法的参数个数可以根据实际情况改变
x,y,z
传入的参数是对应的类型,是字节码对象
返回一个方法对象
invoke()
执行方法
//获取字节码对象
Class<?> aClass = Person.class;
//创建无参构造 对象
Object obj = aClass.newInstance();
//获取 save 方法
Method save = aClass.getDeclaredMethod("save", int.class, String.class);
//传入的两个参数 int String
//将方法 私有可见
save.setAccessible(true);
//执行方法
save.invoke(obj,55,"哦豁!");//后面两个是 方法需要的参数
单例设计模式
class Demo{//饿汉式 单例设计模式
private Demo(){}
private static Demo demo = new Demo();
public static Demo getDemo(){
return demo;
}
}
class Demo2{//懒汉式 单例设计模式
private Demo2(){}
private static Demo2 demo2;
public static Demo2 getDemo2(){
if(demo2 == null)demo2 = new Demo2();
return demo2;
}
}
Socket
网络
应用程序通过socket
发送或接收数据进行远程通话
这就需要一个服务端和客户端
服务端指定一个端口开启连接
ServerSocket()
0~65535
0~1024
已经被系统占用
ServerSocket socket = new ServerSocket(8888);
//开启一个 8888 端口号
接收客户端的连接请求,并建立数据通信通道
accept()
Socket socket = serverSocket.accept();//建立通道
接收数据
getInputStream()
InputStream socketInputStream = socket.getInputStream();
int b;
while((b=socketInputStream.read()) !=1){
System.out.println((char)b);
}
给客户端发送数据
getOutputStream()
socketOutputStream = socket.getOutputStream();
//写入的数据需要转 byte 类型
socketOutputStream.write("Hello world".getBytes());
//把数据刷出去
socketOutputStream.flush();
客户端
客户端需要进行连接端口
Socket()
Socket socket = new Socket("127.0.0.1", 8888);
//127.0.0.1 是 ip地址 8888 是端口号
监听数据
getInputStream()
socketInputStream = socket.getInputStream();
//读取
int b;
while ((b = socketInputStream.read()) != 1) {
System.out.print((char)b+" ");
}
发送数据
//发送数据
stream = socket.getOutputStream();
stream.write("hello".getBytes());
stream.flush();
MD5加密
使用DigestUtils
方法
org.springframework.util.DigestUtils
提供的工具包
使用
DigestUtils.md5DigestAsHex("传入byte类型数组");
//如下
String pwd = "123";
byte[] bytes = pwd.getBytes();
String md5Pwd = DigestUtils.md5DigestAsHex(bytes);
//获取到的 md5Pwd 即加密后 数据
UUID
32位随机数
UUID.randomUUID().toString();