返回类型:int
作用:获取字符串长度
用法: int a=b.lenght();
返回类型:String
作用:获取字符串内某一下标对应的值
用法:char a=b.charAt(下标);
equals(“被比较的字符串”);
返回类型:boolean
作用:判断两字符串是否相同
用法:boolean bool=str.equals("java");
返回类型:int ascill差值 大正同零小负
作用:判断两字符串大小关系,按位依次比较
用法:int ct=str.CompareTo("java");
返回类型:String
作用:将当前对象以字符串的形式返回
用法:System.out.println(date.toString());
返回类型:String
作用:移除字符串两侧的空白字符或其他预定义字符
用法:String newa=a.trim(); //去除字符串a左右的空格
返回类型:int 正确返回下标,错误返回-1
作用:返回所查字符的下标
用法1:int index = str.indexOf("要查找的字符串");//返回字符串在str的下标
用法2:index = str.indexOf("要查找的字符串", 开始查找的下标);
结合:int index = str.indexOf("Java"); //第一次返回的索引
index = str.indexOf("Java", index + 4); //index+4跳过第一次的索引下标
substring(开始索引,结束索引);
返回类型:String 包含开始索引,不包含结束索引 无结束索引即默认打印开始索引后的全部
作用:返回当前字符串中一个连续的片段
用法1:String newStr = str.substring(4);
用法2:String newStr = str.substring(4,7);
返回类型:boolean
作用:检查给定的字符串是否以指定字符串的字符开头
用法:boolean bool=url.startsWith("字符串");
返回类型:boolean
作用:检查给定的字符串是否以指定字符串的字符结尾
用法: boolean bool=url.endsWith("字符串");
replaceAll(“要替换的值”,“新值”)
返回类型:String 失败返回原字符串
作用:在字符串中用一些字符替换另一些字符,只返回新字符串,原字符串不变
用法: String newStr = str.replaceAll("Java","Python");
split(“用以分割字的符串”);
返回类型:Array ‘.’ ‘|’ ‘*’ 这三类要加上"\"
作用:将文本数据按某个字符分割成数组
用法:String[] data = text.split("\n");
全大写:toUpperCase();
全小写:toLowerCase();
返回类型:String
作用:将字符串进行大小写转换
用法:String enName = text.toUpperCase();
字符串转化数字:Integer.parseInt(“字符串”);
用法:int a = Integer.parseInt("100");
数字转字符串:String.valueOf(数字);
用法:String str = String.valueOf(a);
住:数字转字符串也可以:String str = ""+100;
基本格式:
1.必须是对象{}或数组【】
语法规则:
值类型:
值可以是数组对象,就意味着数据和对象可以任意嵌套、任意深度
如果需要处理LocalDateTime的json数据,不管是序列化还是反序列化,都需要在定义的字段上添加注解:
@JsonDeserialize(using = LocalDateTimeDeserializer.class)
@JsonSerialize(using = LocalDateTimeSerializer.class)
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss")
字符串集合序列化
List<String> Str=new ArrayList<>();
Str.add("hello");
Str.add("world");
ObjectMapper mapper=new ObjectMapper(); //创建Jackson的核心对象ObjectMapper
//import com.fasterxml.jackson.core.type.TypeReference;
try {
String jsonstr = mapper.writeValueAsString(Str); //调用writeValueAsString,将指定的对象转换成json
System.out.println(jsonstr);
}catch (Exception e){
System.out.println(e);
}
单个对象序列化{ }
User user=new User();
user.setUserName("tom");
user.setPassword("tom123");
user.setGmtCreated(LocalDateTime.now());
ObjectMapper mapper=new ObjectMapper();
try {
String jonsUser = mapper.writeValueAsString(user);
System.out.println(jonsUser);
}catch (Exception e){
System.out.println(e);
}
集合对象序列化[ ]
List<User> users = new ArrayList<>();
User user=new User();
user.setUserName("tom");
user.setPassword("tom123");
user.setGmtCreated(LocalDateTime.now());
users.add(user);
ObjectMapper mapper=new ObjectMapper();
try {
String jonsUsers = mapper.writeValueAsString(users);
System.out.println(jonsUsers);
}catch (Exception e){
System.out.println(e);
}
将json文件反序列化为User对象
单对象反序列化:{ }
ObjectMapper mapper=new ObjectMapper();
try {
String content = FileUtils.readFileToString(new File("./data/users.json"), "utf-8");//将指定的文件夹中的内容读取出来以String的形式来显示
User user = mapper.readValue(content, User.class);
System.out.println(user.getUserName());
} catch (IOException e) {
e.printStackTrace();
}
将json文件反序列化为List集合
多对象反序列化[{ },{ }]
ObjectMapper mapper=new ObjectMapper();
try {
String content = FileUtils.readFileToString(new File("./data/users.json"), "utf-8");
List<User>users=mapper.readValue(content, new TypeReference<List<User>>() {
});//将Json字符串反序列化为List集合
for (User user : users) {
System.out.println(user.getUserName());
}
} catch (Exception e) {
e.printStackTrace();
}
使用static代码块:
static{
//代码
}
特点:优先执行,只执行一次
将json文件中的数据读取到集合
static{
if (usersFile.exists()) {//判空
try {
String conent = FileUtils.readFileToString(usersFile, "utf-8");//将字符串写入到文件当中
List<User> users1 = mapper.readValue(conent, new TypeReference<List<User>>() {
});
users.addAll(users1); //将集合数据添加到集合
} catch (IOException e) {
e.printStackTrace();
}
}
判断输入数据是否以及存在,存在则return
for (User user : users) {
if(user.getUserName().equals(userNameText)){
notification.setText("username already reg");
notification.open();
return;
}
}
json字符串转化为对象集合
public static List<SongList> songList=new ArrayList<SongList>();
songList= JSONArray.parseArray(content,SongList.class);
json字符串转化为对象数组
SongList[] lists = JSON.parseObject(jsonString, SongList[].class);
实例化:File file = new File("路径");
计算文件大小(字节数):long size = file.length();
配置Apache commons-io
Apache commons-io 是为了简化java Io的操作,可以很轻松的读文件
找到工程文件里的pom.xml文件,在节点里配置如下内容:
commons-io
commons-io
2.10.0
注:是配置依赖的
con=FileUtils.readFileToString(file,“utf-8”); //将指定的文件夹中的内容读取出来以String的形式来显示
con=FileUtils.readLines(file,“utf-8”); //按行读
//实例化一个文件类型的文件,把指向的文件对象(./data/users.txt)存放到实例化的对象里面。
File nameFile = new File("./data/users.txt");
try {
//以字符串形式去读取utf-8编码的nameFile里面的文件,存放到定义好的content对象里
String content = FileUtils.readFileToString(nameFile,"utf-8");
System.out.println(content);
} catch (IOException e) {
e.printStackTrace();
}
}
File usersFile = new File("./data/users.txt");
List<String>users = new ArrayList<>();
try {
//按行读取文件,存放到users集合里面
users = FileUtils.readLines(usersFile,"utf-8");
} catch (Exception e) {
e.printStackTrace();
}
//定义一个User类型的集合
List<User>users1 = new ArrayList<>();
User user;
//遍历users集合
for (String s : users) {
user = new User();
//每一次遍历把遍历到的内容用split分割成字符串,存放到定义的字符串t里面
String[] t= s.split(" ");
//user集合按位取得字符串里的内容
user.setUserName(t[0]);
user.setPassword(t[1]);
//读取时间
user.setGmtCreated(LocalDateTime.parse(strings[2]));
//把本次循环里的user集合添加到users1集合里
users1.add(user);
}
//通过遍历打印users1里面所有用户名
for (User user1 : users1) {
System.out.println(user1.getUserName());
}
FileUtils.writeStringToFile(接受的文件,传输的内容,格式);
将文件以String形式写出FileUtils.writeValueAsString(文件)
将字符串存储为文件,实现永久存储
try {
String conent = mapper.writeValueAsString(users);//转化为json
File file=new File("./data/users.json"); //打开文件
FileUtils.writeStringToFile(file,conent,"utf-8");//写入
} catch (Exception e) {
e.printStackTrace();
}
try
{
// 程序代码
}catch(ExceptionName e1){
//Catch 块
}
捕获异常快捷键:alt+回车
File file=new File("./data/users.txt");
String con=null;
try{
con= FileUtils.readFileToString(file,"utf-8"); //必须捕获异常
}catch(Exception e){
e.printStackTrace();
};
System.out.println(con);
}
TextField 用于处理输入框对象
Button 用于处理按钮行为的对象
Route 用于处理页面地址的对象
VerticalLayout 用于处理页面布局的对象
垂直布局类:VerticalLayout
水平布局类:HorizontalLayout
文本框输入类:TextField
按钮类:Button
输入框上方文字:.setLabel();
输入框内部提示文字:.setPlaceholder();
输入框内部输入的内容:.setValue();
按钮内部文字输入方法:setText
通知类:Notification
通知类的显示方法:.open
通知类的文本输入方法:.setText
通知类的延时方法:.setDuration
声明:com.vaadin.flow.component.textfield.TextField
实例化:TextField field = new TextField();
添加用布局组件,add可以添加任意子组件: add(field);
垂直布局声明:com.vaadin.flow.component.orderedlayout.VerticalLayout
水平布局声明:com.vaadin.flow.component.orderedlayout.HorizontalLayout
输入框上有字:field.setLabel(String label)
输入框中有提示字:field.setPlaceholder(String placeholder)
输入框中有默认值:field.setValue(String valu
public class Todo extends VerticalLayout {//继承与垂直布局类
public Todo() {
TextField userNameField = new TextField();//创建输入框
TextField passWordField = new TextField();
String userName ="UserName";
String passWord ="Password";
userNameField.setLabel(userName);//输入框上的文字
passWordField.setLabel(passWord);//输入框上的文字
userNameField.setPlaceholder("please input userName");//输入框内的提醒
passWordField.setPlaceholder("please input password");//输入框内的提醒
add(userNameField,passWordField);//添加窗口
}
}
声明:com.vaadin.flow.component.button.Button
创建一个保存按钮对象并添加信息:Button addButton = new Button(“提示信息”);
添加提示信息也可以这样:addButton.setText(“提示信息”);
添加:add(addButton)
给 loginButton 添加一个点击事件:loginButton.addClickListener(click->{}); //click任意
Button loginButton = new Button("Login");//创建按钮并定义按钮名称
loginButton.addClickListener(click->{// 给 loginButton 添加一个点击事件
String text = userNameField.getValue();//获取输入框里的内容赋值给text
String text1 = passwordField.getValue();
System.out.println(text+"/"+text1+"login"+"fail");
add(loginButton);//添加按钮
});
用于登录事件执行后的账户密码判断
loginButton.addClickListener(click -> {
String userNameText = userNameField.getValue();
if ("admin".equals(userNameText)){
System.out.println("用户名正确");
}
});
声明:com.vaadin.flow.component.notification.Notification
实例化:Notification no=new Notification();
显示的提示内容:no.setText("success");
显示时长设置(1000毫秒=1秒): no.setDuration(3000);
显示提示框: no.open();
Notification notification = new Notification();
notification.setDuration(3000);
notification.setText("success");
notification.open();
支持多界面,“”中填写地址
@Route("/reg.html")
装箱
基本类型—>包装类型
拆箱
包装类型—>基本类型
手动装箱
int i=1;
Integer integer = new Integer(i);
//或者
Integer integer1 = Integer.valueOf(i);
手动拆箱
int i1 = integer1.intValue();
自动装箱
int i=1;
Integer integer=i; //其底层使用的是Integer.valueOf方法 jdk5以后支持
自动拆箱
int n=integer; //其底层使用的是 integer1.intValue方法
Integer–>String
Integer i = 100;
//法一
String str1 = i + "";
//法二
String str2 = i.toString();
//法三
String str3 = String.valueOf(i);
String -->Integer–>
String str="123";
//法一
Integer i1=new Integer(str); //构造器
//法二
Integer i2=Integer.parseInt(str); //自动装箱
下列输出结果?
Integer i1=new Integer(1);
Integer i2=new Integer(1);
System.out.println(i1==i2); //false 两个对象比较 ==判断的是引用
Integer i3=1;
Integer i4=1;
System.out.println(i3==i4); //true 其底层用的是 Integer.valueOf()
Integer i5=128;
Integer i6=128;
System.out.println(i5==i6); //false 其底层用的是 Integer.valueOf()
原因 --Integer.valueOf()
//这是valueOf源码 This method will always cache values in the range -128 to 127
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
可以看到,valueOf方法有一个值判断,如果不在-128 to 127,则new一个对象。
String实现了Serializable接口 可以串行化 -即可以在网络传输
String被final修饰,不能被改变(地址而不是字符内容,每次修改都是产生一个新的String)
String 有属性private final byte[] value; 用于存放字符串内容
题一:判断下面代码输出结果
Person p1=new Person();
p1.setName("he");
Person p2=new Person();
p2.setName("he");
System.out.println(p1.getName().equals(p2.getName())); //true 比较的是内容
System.out.println(p1.getName()==p2.getName());//true 指向的都是常量池中同一个字符串 (地址相同)
System.out.println(p1.getName()=="he");//true 指向同一个常量池字符串 (地址相同)
内存图
题二:以下语句创建了几个对象
String str="he";
str="hehe"; //两个 每次修改都会创建新的String 对象的引用改变了
内存图
题三:创建了几个对象
String a="hello"+"abc";
//只有一个
//String a="hello"+"abc"; 编译器优化等价于 String a="helloabc";
题四: 创建了几个对象
String a="hello";
String b="abc";
String c=a+b; //三个
/*String c=a+b;实际上进行了如下操作:
1.先创建一个StringBuilder sb=StringBuilder()
2.执行 sb.append("hello");
3.执行 sb.append("abc");
4.String c=sb.toString()
最后实际上是 c指向堆种的对象(String)value[] ->池中“helloabc"
*/
内存图
注意:
创建方法
//创建一个大小为16的 char[] ,用于存放字符内容
StringBuffer stringBuffer1=new StringBuffer();
//通过构造器指定 char[] 大小
StringBuffer stringBuffer2=new StringBuffer(50);
//通过给一个Strintg 创建StringBuffer char[] 大小就是 String.length()+16
StringBuffer stringBuffer3=new StringBuffer("hello");
StringBuffer---->String
StringBuffer stringBuffer=new StringBuffer("hello");
//法一
String s = stringBuffer.toString();
//法二
String s1=new String(stringBuffer);
String---->StringBuffer
String str="hello";
//法一
StringBuffer stringBuffer1=new StringBuffer(str);
//法二
StringBuffer stringBuffer2 = new StringBuffer().append(str);
增
StringBuffer stringBuffer=new StringBuffer("hello");
stringBuffer.append(" ");
stringBuffer.append("world").append(10).append(true);
System.out.println(stringBuffer); //结果:hello world10true
删
stringBuffer.delete(0,5); //删除索引为>=start &&
改(替换)
stringBuffer.replace(0,5,"你好"); //替换0-4处字符为你好
查(查找指定子串第一次出现的索引)
int indexOf = stringBuffer.indexOf("world");
插入(在指定位置插入,原位置字符自动后移)
stringBuffer.insert(0,"hello");
题目一:将空String转换为StringBuffer,会输出什么
String str=null;
//StringBuffer stringBuffer=new StringBuffer(str); 注意:使用构造器会报错,因为构造器设置@NotNull
stringBuffer.append(str);
System.out.println(stringBuffer); //null
System.out.println(stringBuffer.length()); //4
原因------>源码
// stringBuffer.append源码 若传入值为null,则调用父类append
public synchronized StringBuffer append(String str) {
toStringCache = null;
super.append(str);
return this;
}
//其父类 AbstractStringBuilder的append源码 若值为null 则调用appendNull方法
public AbstractStringBuilder append(String str) {
if (str == null) {
return appendNull();
}
int len = str.length();
ensureCapacityInternal(count + len);
putStringAt(count, str);
count += len;
return this;
}
//appendNull方法源码
private AbstractStringBuilder appendNull() {
ensureCapacityInternal(count + 4);
int count = this.count;
byte[] val = this.value;
if (isLatin1()) {
val[count++] = 'n';
val[count++] = 'u';
val[count++] = 'l';
val[count++] = 'l';
} else {
count = StringUTF16.putCharsAt(val, count, 'n', 'u', 'l', 'l');
}
this.count = count;
return this;
}
和StringBuffer用法完全一致
可以使用toString遍历数组
int[] i = {1, 2, 3, 4};
System.out.println(Arrays.toString(i));
toString源码
public static String toString(int[] a) {
if (a == null)
return "null";
int iMax = a.length - 1;
if (iMax == -1)
return "[]";
StringBuilder b = new StringBuilder();
b.append('[');
for (int i = 0; ; i++) {
b.append(a[i]);
if (i == iMax)
return b.append(']').toString();
b.append(", ");
}
}
Arrays.sort方法排序(默认排序升)
int[] i = {1, 2, 3, 4,-5,-3,0};
Arrays.sort(i); //排序
System.out.println(Arrays.toString(i)); //[-5, -3, 0, 1, 2, 3, 4]
定制排序(排序升降)
Integer[] arr={1, 2, 3, 4,-5,-3,0};
Arrays.sort(arr,new Comparator(){
@Override
public int compare(Object o1, Object o2) {
Integer i1= (Integer) o1;
Integer i2= (Integer) o2;
return i1-i2;
}
});
System.out.println(Arrays.toString(arr));
源码分析
//(1)执行
Arrays.sort(arr,new Comparator());
//(2)最终到了
private static <T> void binarySort(T[] a, int lo, int hi, int start,Comparator<? super T> c) {
····
while (left < right) {
int mid = (left + right) >>> 1;
if (c.compare(pivot, a[mid]) < 0)
right = mid;
else
left = mid + 1;
}
····
}
//(3)执行到 binarySort方法的代码,会根据动态绑定机制 c.compare()执行我们传入的匿名内部类compare()
new Comparator(){
@Override
public int compare(Object o1, Object o2) {
Integer i1= (Integer) o1;
Integer i2= (Integer) o2;
return i1-i2;
}
}
//(4)public int compare(Object o1, Object o2) 返回值是否大于小于0会影响排序的结果
前提:数组有序
Integer[] arr={1, 2, 3, 4};
int i1 = Arrays.binarySearch(arr, 3);
System.out.println(i1);
从arr数组中,拷贝arr.length个元素到arr1
如果拷贝长度大于arr.length 就在新数组后面加null 拷贝长度小于零报异常
Integer[] arr={1, 2, 3, 4,-5,-3,0};
Integer[] arr1=Arrays.copyOf(arr,arr.length);
使用100 去填充arr
Integer[] arr={1, 2, 3, 4,-5,-3,0};
Arrays.fill(arr,100);
System.out.println(Arrays.toString(arr)); //[100, 100, 100, 100, 100, 100, 100]
//获取当前时间
Date date = new Date();
System.out.println(date);
//通过毫秒数获取时间
Date date = new Date(1008666);
System.out.println(date);
//创建SimpleDateFormat对象设置格式
SimpleDateFormat simpleDateFormat=new SimpleDateFormat("YYYY年MM月dd日 hh:mm:ss E");
String format = simpleDateFormat.format(new Date()); //调用format将当前日期转换为字符串
System.out.println(format);
String str="2023年06月07日 09:46:13 周三";
SimpleDateFormat simpleDateFormat=new SimpleDateFormat("YYYY年MM月dd日 hh:mm:ss");
Date parse = simpleDateFormat.parse(str); //转换的date是国外格式 Sun Jan 01 09:46:13 CST 2023
//如果想输出自定义格式,再使用simpleDateFormat.format转换就可以了
String format = simpleDateFormat.format(parse);
System.out.println(format);
注意:1.在将String转换为date,使用的SimpleDateFormat格式要和String格式一样,否则抛出转换异常
2.在将String转换为date,转换的date是国外格式
//Calendar是一个抽象类,并且构造器是private 只能getInstance获取实例,不能new
Calendar instance = Calendar.getInstance();//创建日历类对象
System.out.println(instance);
/*会输出大量字段:java.util.GregorianCalendar[time=1686104901715,areFieldsSet=true,areAllFieldsSet=true,lenient=true,zone=sun.util.calendar.ZoneInfo[id="Asia/Shanghai",offset=28800000,dstSavings=0,useDaylight=false,transitions=19,lastRule=null],firstDayOfWeek=1,minimalDaysInFirstWeek=1,ERA=1,YEAR=2023,MONTH=5,WEEK_OF_YEAR=23,WEEK_OF_MONTH=2,DAY_OF_MONTH=7,DAY_OF_YEAR=158,DAY_OF_WEEK=4,DAY_OF_WEEK_IN_MONTH=1,AM_PM=0,HOUR=10,HOUR_OF_DAY=10,MINUTE=28,SECOND=21,MILLISECOND=715,ZONE_OFFSET=28800000,DST_OFFSET=0]*/
获取日历对象的某个字段
System.out.println("年"+instance.get(Calendar.YEAR));
System.out.println("月"+instance.get(Calendar.MONTH));
System.out.println("日"+instance.get(Calendar.DAY_OF_MONTH));
System.out.println("明日"+instance.get(Calendar.DAY_OF_MONTH)+1);
//Calendar没有专门的格式化方法,需要程序员直接组合
System.out.println(instance.get(Calendar.YEAR)+"年"+
instance.get(Calendar.MONTH)+"月"+
instance.get(Calendar.DAY_OF_MONTH)+"日");
注意:如果想以24小时制显示时间,Calendar.HOUR 改为Calendar.HOUR_OF_DAY
日期:yyyy-MM-dd
时间:HH:mm:ss
带毫秒的时间:HH:mm:ss.SSS
日期和时间:yyyy-MM-dd’T’HH:mm:ss
带毫秒的日期和时间:yyyy-MM-dd’T’HH:mm:ss.SSS
用法:LocalDate now = LocalDate.now();// 得到当前的完整日期(不包括时间)
System.out.println(now.toString());// 打印出日期
得到当前年:int year = time.getYear();
得到当前月: int month = time.getMonth().getValue();
得到当前日:int day = time.getDayOfMonth();
得到当前周: int dayOfWeek = time.getDayOfWeek().getValue();
得到当前完整时间:LocalDateTime loc=LocalDateTime.now(); //日期+时间
LocalDateTime localDateTime=LocalDateTime.now();
System.out.println(localDateTime);
LocalDateTime localDateTime=LocalDateTime.now();
DateTimeFormatter dateTimeFormatter=DateTimeFormatter.ofPattern("YYYY年MM月dd日 hh:mm:ss");
String format = dateTimeFormatter.format(localDateTime);
System.out.println(format);
天数加减 plusDays(天数) minusDays(天数)
LocalDate leaveTime = time.plusDays(days); //加days天
LocalDate leaveTime=time.minusDays(days); //减去days天
月数加减 plusMonths(月数) minusMonths(月数)
LocalDate leaveTime = time.plusMonths(mons);//加mons天
LocalDate leaveTime=time.minusMonths(mons);//减mons天
年数加减 plusYears(年数) minusYears(年数)
LocalDate leaveTime = time.plusYears(years); //加years年
LocalDate leaveTime=time.minusYears(years); //减years年
周数加减 plusWeek(周数) minusWeek(周数)
LocalDate leaveTime = time.plusWeek(weeks); //加weeks周
LocalDate leaveTime=time.plusWeek(weeks); //减weeks周
返回boolean
boolean bool=time1.isBefore(time2); //之前
boolean bool=time1.isAfter(time2); //之后
boolean bool=time1.isEqual(time2); //当天
集合分为单列集合Collection 和双列集合Map
Iterator对象称为迭代器,主要用于遍历Collection集合中的元素,所有实现Collection接口的集合类都有iterator方法
Iterator仅用于遍历集合,本身并不存放对象
如果要二次遍历,需要重置Iterator 即新创一个
ArrayList list=new ArrayList<>();
list.add(new Book("java基础","余胜军"));
list.add(new Book("数据结构","jacker"));
Iterator iterator = list.iterator();
while (iterator.hasNext()) {
Object next = iterator.next();
System.out.println(next);
}
//book{name='java基础', author='余胜军'}
//book{name='数据结构', author='jacker'}
增强for循环可以代替Iterator迭代器
增强for循环就是简化的Iterator(其底层就是Iterator),本质一样,只能用于遍历集合和数组.
for (Object o : list) {
System.out.println(o);
}
List添加基础数据类型时,会有一个自动装箱的过程,即会把基础类型转换成对应的包装类
常用方法
//add 添加单个元素
list.add("w");
list.add("b");
list.add(10); //自动装箱为包装类Integer
//addAll 添加多个元素
ArrayList list1=new ArrayList<>();
list1.add("ll");
list.addAll(list1); //所有Collection的子类都可以加入 返回bool
list.addAll(0,list1); //所有Collection的子类都可以加入 从下标0开始将list1所有元素插入 返回bool
//size 获取元素个数
System.out.println(list.size());
//isEmpty 判断是否为空
System.out.println(list.isEmpty());
//remove 删除指定元素
list.remove(0); //删除第一个元素
list.remove("w"); //删除指定元素
//removeAll 删除多个元素 返回bool
list.removeAll(list1); //所有Collection的子类
//clear 清空
list.clear();
//contains 查找元素是否存在 返回bool
System.out.println(list.contains("w"));
//containsAll 查找多个元素是否存在
list.containsAll(list1); //所有Collection的子类
//indexOf 返回首次出现字符的下标
System.out.println(list.indexOf("java基础"));
//lastIndexOf 返回最后出现字符的下标
System.out.println(list.lastIndexOf("java基础"));
//set 替换指定位置字符 索引必须存在
list.set(0,"数据结构");
//subList 返回指定的子集合 【 )
System.out.println(list.subList(0, 2));
ArrayList底层结构:
ArrayList底层是Object数组。若是无参构造创建,则初始容量为0,第一次添加则扩容到10,再次扩容则是1.5倍。若是指定大小构造,则初始容量为指定大小,再次扩容也是1.5倍。
ArrayList 线程不安全
Vector 【V ke te】
Vector底层是Object数组。若是无参构造创建,则初始容量为10,再次扩展是2倍。若是指定大小构造,再次扩容直接2倍。
Vector 线程安全
LinkedList
LinkdList底层是双向循环链表,可以添加任意元素,可重复
LinkedList线程不安全
Set无序不可重复,最多允许一个null对象,无索引,只能使用迭代器取得所有元素,逐一遍历
插入多个null不会报错,只是加入失败
Set有两个子类 HashSet TreeSet
HashSet
对象加入hashSet时,hashSet会先计算对象的hashCode值判断对象加入的位置,如果发现有值,则调用equals方法来检测两个对象是否相同,如果相同就加入失败,如果不同,就散列到其他位置,这样减少了equals的次数,提高了执行速度
–则 未重写equals的类可以new多个相同内容对象加入HashSet ,String就不可以new多个相同内容对象加入HashSet
HashSet hashSet=new HashSet<>();
hashSet.add(new Book("111","111"));//ok
hashSet.add(new Book("111","111")); //ok
hashSet.add(new String("111"));//ok
hashSet.add(new String("111"));//no
hashSet底层是hashMap
TreeSet
有序(在未作任何处理时无序)
使用无参构造器创建TreeSet时,是无序的
使用TreeSet提供的构造器,可以传入一个比较器,并指定排序规则
Map用于保存具有映射关系的数据
Map 中key不允许重复,velue可以重复 (key不允许重复原因和set一样,要计算hashCode值 ,所以也是无序的 )
Map中 hashMap允许null值 HashTable不允许null值
hashMap 线程不安全 ,hashTable线程安全
Map原理
k-v 最后是 HashMap$Node node=newNode(hash,key,value,null)
k-v为了方便程序员的遍历,还会创建EntrySet集合,该集合存放的元素类型Entry ,而一个Entry对象就有 k,v
EntrySet> 即:transient Set> entrySet;
entrySet中,定义的类型是Map.Entry ,但实际上还是存的HashMap$Node
---把HashMap$Node对象存放到entrySet 是为了方便遍历
Map遍历
Map<String, Integer> buy=new HashMap<>();
buy.put("苹果手机", 2);
buy.put("智能手表", 1);
Set<Map.Entry<String, Integer>> entries = buy.entrySet(); //转化为Set集合
for (Map.Entry<String, Integer> entry : entries) { //增强for遍历
System.out.println(entry.getKey()+":"+entry.getValue());
}
HashMap
HashMap基于哈希表实现,通过使用键的hashCode()来确定键值对的存储位置,无序不可重复
HashMap线程不安全;
HashMap允许key和value为null(k为null只能有一个), HashTable不允许;
HashTable
底层和HashMap一样,基于哈希表实现,无序不可重复
HashTable线程安全;
HashTable不允许k-v为null
treeMap
TreeMap基于红黑树实现,它根据键的自然顺序或者Comparator 【kan pai er te】来组织键值对 ,自然有序不可重复
treeMap线程不安全;
treeMap不允许k为null,但允许v为null
泛型,即“参数化类型”。
泛型的作用:在类声明时通过一个标识表示类中某个属性的类型以及方法的返回类型
往集合添加数据并遍历时
List list=new ArrayList();
传统方法的缺点:
不能对加入到集合的数据类型进行约束
遍历时,需要类型转化,如果集合数据量大,效率低
使用泛型:
List<Book> list=new ArrayList<Book>();
对加入集合的数据类型进行约束,非Book类加入即报错
泛型的作用:在类声明时通过一个标识表示类中某个属性的类型以及方法的返回类型
class Book<E>{
private E name; //E表示name的数据类型,该数据类型在定义Book对象时指出,即在编译期间确定E是什么类型
private E author;
public E getName() {
return name;
}
}
//定义
Book<String> book=new B<String>();
Component类提供两个和绘图相关最重要的方法:
画圆案例
先定义一个MyPanel,继承 JPanel 类,画图形 【Panel -画板】 【paint-颜料】 【Graphics-绘画】
//画板类
public class MyPanel extends JPanel {
//绘画方法
@Override
public void paint(Graphics g) {
//调用父类的方法完成初始化
super.paint(g);
//画一个圆 x坐标,y坐标(都是距左上角的像素数),宽度,高度
g.drawOval(10,10,100,100);
}
}
主程序 继承JFrame 【 JFrame-窗口】
//窗口
public class DrawCircle extends JFrame {
//定义一个画板
private MyPanel mp = null;
public static void main(String[] args) {
//构造
new DrawCircle();
}
public DrawCircle() {
//初始化面板
mp = new MyPanel();
//把面板放入窗口
this.add(mp);
//设置窗口大小
this.setSize(400, 400);
//设置可视化
this.setVisible(true);
//设置叉掉窗口,则退出程序
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
问题:只实例化了MyPanel,并未调用paint方法,为什么paint方法会执行?
原因:当组件第一次在屏幕显示时,程序会自动调用paint方法来绘制组件
在以下情况paint方法将会被调用:
- 窗口最小化后再最大化
- 窗口大小发生改变
- repaint函数被调用
Graphics类可以理解为画笔,为我们提供了各种绘制图形的方法
//画直线 起点(x,y) -终点(x,y)
drawLine(int x1,int y1,int x2,int y2)
//画矩形边框 距左上角的距离(x,y)
drawRect(int x,int y,int width,int height)
//画椭圆边框
drawOval(int x,int y,int width,int height)
//填充矩形
fillRect(int x,int y,int width,int height)
//填充椭圆
fillOval(int x,int y,int width,int height)
//设置画笔的字体
setFont(Font font)
---g.setFont(new Font("隶书",Font.BOLD,50)); 【字体名字,是否粗体,大小】
//设置画笔的颜色
setColor(Color c)
---g.setColor(Color.BLACK);
//画图片
drawImage(Image img,int x,int y,...)
//画字符串
drawString(String str,int x,int y)
画图片 drawImage(Image img,int x,int y,…) 【图片,坐标x,坐标y,分辨率x,分辨率y,this】
//先获取图片 (固定写法)
Image image = Toolkit.getDefaultToolkit().getImage(Paint.class.getResource("/.bg.png"));
//画图片
drawImage(image,10,10,1080,960,this)
实例-绘制坦克
演示小球通过键盘上下左右控制移动轨迹
先定义一个MyPanel,继承 JPanel 类,画图形, 再实现KeyListener 接口 事务处理 用于获取键盘信息
//继承 JPanel类 绘画 实现 KeyListener 接口 事务处理
public class MyPanel extends JPanel implements KeyListener {
//坐标
private static int x=10;
private static int y=10;
//绘画
@Override
public void paint(Graphics g) {
super.paint(g);
g.fillOval(x, y, 30, 30);
}
//有字符输出,该方法会触发
@Override
public void keyTyped(KeyEvent e) {
}
//当键盘键按下,该方法触发
@Override
public void keyPressed(KeyEvent e) {
System.out.println((char)e.getKeyCode()+"键被按下");
//根据按下不同键,来处理移动
if(e.getKeyCode()==KeyEvent.VK_DOWN){ //KeyEvent.VK_DOWN向下箭头
y++;
}
if(e.getKeyCode()==KeyEvent.VK_UP){ //KeyEvent.VK_UP 向上
y--;
}
if(e.getKeyCode()==KeyEvent.VK_LEFT){//KeyEvent.VK_LEFT 向左
x--;
}
if(e.getKeyCode()==KeyEvent.VK_RIGHT){//KeyEvent.VK_RIGHT 向右
x++;
}
//面版重绘
this.repaint();
}
//当键盘键释放(松开),该方法触发
@Override
public void keyReleased(KeyEvent e) {
}
}
主程序 继承JFrame
//窗口
public class BallMove extends JFrame {
//定义画板
MyPanel myPanel=null;
public BallMove(){
//初始化面板
myPanel = new MyPanel();
//把面板放入窗口
this.add(myPanel);
//设置窗口监听键盘事件 ,即可以监听面板myPanel发生的键盘事件
this.addKeyListener(myPanel);
//设置窗口大小
this.setSize(400, 400);
//设置可视化
this.setVisible(true);
//设置叉掉窗口,则退出程序
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public static void main(String[] args) {
new BallMove();
}
}
注意点:
绘画类要继承JPanel 且实现KeyListener
在执行了KeyListener 中事务方法时,要调用 this.repaint() 方法重绘
主体窗口要设置监听 this.addKeyListener()
创建文件对象相关方法
new File(String pathname) //根据路径构建一个File对象
new File(File parent,String child) //根据父目录文件+子路径构建
new File(String parent,String child) //根据父目录+子路径构建
方式一 根据路径构建一个File对象
String filePath = "D:\\news1.txt";
//构建File对象
File file = new File(filePath);
try {
//创建文件
file.createNewFile();
System.out.println("文件创建成功");
} catch (IOException e) {
System.out.println("文件创建失败");
}
方式二 根据父目录文件+子路径构建
//构建File对象作为父目录
File file = new File("D:\\");
String fileName = "news2.txt";
//构建完整的File
File fileNow=new File(file,fileName);
try {
//创建文件
fileNow.createNewFile();
System.out.println("文件创建成功");
} catch (IOException e) {
System.out.println("文件创建失败");
}
方式三 根据父目录+子路径构建
String filePath="D:\\";
String fileName = "news3.txt";
File fileNow=new File(filePath,fileName);
try {
//创建文件
fileNow.createNewFile();
System.out.println("文件创建成功");
} catch (IOException e) {
System.out.println("文件创建失败");
}
注意
File fileNow=new File(); 并不会直接创建文件,这只是构建出一个java的File对象 只有执行 fileNow.createNewFile(); 才能创建对象
File fileNow=new File("D:\\news1.txt");
System.out.println("文件名=" + fileNow.getName());
System.out.println("文件路径="+fileNow.getPath());
System.out.println("文件绝对路径"+fileNow.getAbsolutePath());
System.out.println("文件父目录="+fileNow.getParent());
System.out.println("文件大小(字节)"+fileNow.length());
System.out.println("文件是否存在(bool)"+fileNow.exists());
System.out.println("判断是不是一个文件(bool)"+fileNow.isFile());
System.out.println("判断是不是一个目录(bool)"+fileNow.isDirectory());
mkdir 创建一级目录 mkdirs 创建多级目录 delete 删除空目录或文件
判断文件是否存在,存在则删除,否则就创建
File fileNow=new File("D:\\news1.txt");
if(!fileNow.exists()){
System.out.println("文件不存在");
try {
fileNow.createNewFile();
System.out.println("文件创建成功");
} catch (IOException e) {
System.out.println("文件创建失败");
}
}else {
System.out.println("文件存在");
if(fileNow.delete()){
System.out.println("文件删除成功");
}
else {
System.out.println("文件删除失败");
}
}
判断目录是否存在,存在就删除,不存在则创建 mkdir
File fileNow=new File("D:\\news1");
if(!fileNow.exists()){
System.out.println("目录不存在");
if (fileNow.mkdir()) {
System.out.println("目录创建成功");
}else {
System.out.println("目录创建失败");
}
}else {
System.out.println("目录存在");
if(fileNow.delete()){
System.out.println("目录删除成功");
}
else {
System.out.println("目录删除失败");
}
}
判断多级目录是否存在,存在就删除,不存在则创建 mkdirs
File fileNow=new File("D:\\news1\\news2");
if(!fileNow.exists()){
System.out.println("目录不存在");
if (fileNow.mkdirs()) {
System.out.println("目录创建成功");
}else {
System.out.println("目录创建失败");
}
}else {
System.out.println("目录存在");
if(fileNow.delete()){
System.out.println("目录删除成功");
}
else {
System.out.println("目录删除失败");
}
}
I/O 是Input/Output的缩写 用于处理数据传输
Java程序中,对于数据的输入输出操作以 流(stream)的方式进行
java.io包下提供各种“流”类和接口,用于获取不同种类的数据,并通过方法输入或输出数据
输入输出
输入input:读取外部数据(磁盘)到程序(内存)
输出output:将程序(内存)数据输出到磁盘
流的分类
按操作数据单位不同分为:字节流(8 bit 二进制文件)字符流(按字符 文本文件)
按数据流的流向不同分为:输入流、输出流
按流的角色不同分为:节点流、处理流/包装流
抽象基类 | 字节流 | 字符流 |
---|---|---|
输入流 | InputStream | Reader |
输出流 | OutputStream | Writer |
Java的IO流共涉及40多个类,实际上非常规则,都是从如上4个抽象基类派生的
由这四个类派生出来的子类名称都是以其父类名作为子类后缀名
InputStream抽象类是所有类字节输入流的超类
InputStream常用的子类:
FileInputStream (字节输入流 文件---->程序 )
// read()单字节读取
public static void main(String[] args) {
int readData = 0;
FileInputStream fileInputStream = null;
try {
//创建FileInputStream 对象,用于读取文件
fileInputStream = new FileInputStream("D:\\news1.txt");
//循环读取 read方法每次从该输入流读取一个字节的数据,如果返回-1,表示读取完毕
while ((readData = fileInputStream.read()) != -1) {
//转换为char输出
System.out.println((char) readData);
}
} catch (IOException e) {
System.out.println(e);
} finally {
//释放资源 close
try {
fileInputStream.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
-----------------------------------------------------------------------------------------------
//read(byte[] b)多字节读取
public static void main(String[] args) {
byte[] b = new byte[8];
FileInputStream fileInputStream = null;
try {
//创建FileInputStream 对象,用于读取文件
fileInputStream = new FileInputStream("D:\\news1.txt");
int readDataLength = 0;
//循环读取 read(byte[] b)方法每次从该输入流读取8个字节的数据,如果返回-1,表示读取完毕
while ((readDataLength = fileInputStream.read(b)) != -1) {
//转换为String输出
System.out.print(new String(b, 0, readDataLength));
}
} catch (IOException e) {
System.out.println(e);
} finally {
//释放资源 close
try {
fileInputStream.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
FileOutputStream (字节输出流 程序—>文件)
public static void main(String[] args) {
//创建FileOutputStream 对象
FileOutputStream fileOutputStream = null;
try {
//打开文件一(会覆盖原文件数据) 没有自动创建
// fileOutputStream = new FileOutputStream("D:\\news5.txt");
//打开文件二 (不覆盖原有数据,在其后追加)
fileOutputStream = new FileOutputStream("D:\\news5.txt", true);
//写入一个字符
fileOutputStream.write('a');
//写入字符串 getBytes把字符串转换为字节数组
fileOutputStream.write("bcde".getBytes());
} catch (Exception e) {
System.out.println(e);
} finally {
try {
//释放资源
fileOutputStream.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
思路:1.创建文件输入流,将文件读取到程序中
2.创建文件输出流,将程序中文件输出到指定位置
3.可以一边读,一边写
public static void main(String[] args) {
//创建输入流对象
FileInputStream fileInputStream = null;
//创建FileOutputStream 对象
FileOutputStream fileOutputStream = null;
//创建byte数组,用于设置一次读取字节数
byte[] bytes = new byte[1024];
try {
//指定输入流文件
fileInputStream = new FileInputStream("D:\\news1.txt");
//指定输出流文件
fileOutputStream = new FileOutputStream("D:\\news11.txt", true);
//用于记录读取长度
int readDataLength = 0;
//输入流输入到程序
while ((readDataLength = fileInputStream.read(bytes)) != -1) {
//输出流输出到文件 必须这个方法,不可以直接fileOutputStream.write(bytes);
fileOutputStream.write(bytes, 0, readDataLength);
}
} catch (Exception e) {
System.out.println(e);
} finally {
try {
//释放资源
fileOutputStream.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
只要以Reader、Writer为后缀的都是字符流
FileReader输入流:
- new FileReader(File / String); 创建
- read()每次读取单个字符返回,到文件末尾返回-1
- read(char[ ]) 批量读取多个字符到数组返回,到文件末尾返回-1
- new String(char[ ])将char[ ]转换成String
- new String(char [ ],off ,len)将char[ ]的指定部分转换成String
public static void main(String[] args) {
int readData = 0;
char buf[]=new char[8];
FileReader fileReader = null;
try {
//FileReader 对象,用于读取文件
fileReader = new FileReader("D:\\news1.txt");
//循环读取 read(buf),如果返回-1,表示读取完毕
while ((readData = fileReader.read(buf)) != -1) {
//可以直接输出 new String(buf,0,readData)是避免最后一次取出不足8位
System.out.println(new String(buf,0,readData));
}
} catch (IOException e) {
System.out.println(e);
} finally {
//释放资源 close
try {
fileReader.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
FileWriter 输出流:
- new FileWriter( File / String) 覆盖模式写入文件
- new FileWriter(File / String , true) 追加模式写入文件
- writer(int) 写入单个字符
- writer(char [ ]) 写入指定数组
- writer(char[ ],off,len) 写入指定数组的指定部分
- writer(string) 写入整个字符串
- writer(string,off,len) 写入字符串的指定部分
相关API :String类— toCharArray:将String转换为char[ ]
public static void main(String[] args) {
//创建FileWriter 对象
FileWriter fileWriter = null;
try {
//打开文件一(会覆盖原文件数据) 没有自动创建
// fileWriter = new FileWriter("D:\\news5.txt");
//打开文件二 (不覆盖原有数据,在其后追加)
fileWriter = new FileWriter("D:\\news5.txt", true);
//写入一个字符
fileWriter.write('a');
//写入字符串 getBytes把字符串转换为字节数组
fileWriter.write("bcde");
//写入数组
char[] chars = {1, 2};
fileWriter.write(chars);
//写入指定数组的指定部分
fileWriter.write(chars, 0, 1);
//写入字符串的指定部分
fileWriter.write("abcd", 0, 2);
} catch (Exception e) {
System.out.println(e);
} finally {
try {
//释放资源
fileWriter.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
节点流与处理流的区别与联系:
节点流是底层流(低级流),可以直接跟数据源连接,处理流包装节点流使用了修饰器设计模式,不直接与数据源连接
处理流包装节点流,既可以消除不同节点流的实现差异,也可以提供更方便的方法来完成输入输出
处理流的功能:
性能提高 --主要以增加缓冲的方式提高输入输出效率
操作便捷 --处理流提供一系列便捷方法来一次输入输出大批量数据
BufferedReader
public static void main(String[] args) throws IOException {
//创建BufferedReader FileReader将文件转换为字符流
BufferedReader bufferedReader=new BufferedReader(new FileReader("D:\\\\news5.txt"));
//readLine 按行读取 当返回null,表示读取结束
String read=null;
while (( read=bufferedReader.readLine())!=null){
System.out.println(read);
}
//关闭资源
bufferedReader.close();
}
BufferedWriter
public static void main(String[] args) throws IOException {
//创建BufferedWriter FileWriter将文件转换为字符流 true表追加(默认覆盖)
BufferedWriter bufferedWriter=new BufferedWriter(new FileWriter("D:\\\\news5.txt",true));
//写入
bufferedWriter.write("hello");
//需要插入换行符表输出结束
bufferedWriter.newLine();
//关闭资源
bufferedWriter.close();
}
BufferedInputStream
public static void main(String[] args) throws IOException {
//创建BufferedInputStream 对象 FileInputStream转换为字节流
BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream("D:\\\\news5.txt"));
//IO读取
byte[] bytes = new byte[1024];
int readLen = 0;
while ((readLen = bufferedInputStream.read(bytes)) != -1) {
//转换为String输出
System.out.println(new String(bytes,0,readLen));
}
//关闭资源
bufferedInputStream.close();
}
BufferedOutPutStream
public static void main(String[] args) throws IOException {
//创建BufferedOutputStream对象
BufferedOutputStream bufferedOutputStream=new BufferedOutputStream(new FileOutputStream("D:\\\\news5.txt",true));
//写入
bufferedOutputStream.write('a');
//关闭资源
bufferedOutputStream.close();
}
对象处理流的意义:在保存一个数据的同时,保存这个数据的类型
通俗说就是保存一个dog类型的数据,在读取时,也是以dog形式读出。就是能将基本数据类型或对象进行序列化和反序列化操作
序列化和反序列化
- 序列化就是在保存数据时,保存数据的值和数据类型
- 反序列化就是在恢复数据时,恢复数据的值和数据类型
- 需要让某个对象支持序列化机制,必须让其类是可序列化的,为了让某个类是可序列化的,该类必须实现两个接口之一:Serializable (标记接口,其内没有任何方法,常用)、Externalizable(继承于Serializable 有方法需要实现)
ObjectOutputStream 提供序列化功能
ObjectInputStream 提供反序列化功能
ObjectInputStream(InputStream)
ObjectInputStream()
ObjectOutputStream实例:保存一个Dog对象到data.dat文件中
public class DogObjectOutputStream {
public static void main(String[] args) throws IOException {
//创建 ObjectOutputStream 对象 序列化
ObjectOutputStream objectOutputStream=new ObjectOutputStream(new FileOutputStream("D:\\data.dat",true));
//序列化数据到目标文件 自动装箱
objectOutputStream.writeInt(100); //int--->Integetr (实现了Serializable)
objectOutputStream.writeChar('a'); //char--->Character (实现了Serializable)
objectOutputStream.writeUTF("你好"); //String (实现了Serializable)
objectOutputStream.writeBoolean(true);//boolean--->Boolean (实现Serializable)
objectOutputStream.writeDouble(10.5); //double--->Double (实现Serializable)
//保存一个java对象
objectOutputStream.writeObject(new Dog("haha",5));//Dog实现了Serializable
//关闭资源
objectOutputStream.close();
}
}
//必须实现Serializable 否则报错
class Dog implements Serializable{
String name;
int age;
public Dog(String name, int age) {
this.name = name;
this.age = age;
}
}
ObjectInputStream 实例读取出Dog对象
public class DogObjectInputStream {
public static void main(String[] args) throws IOException, ClassNotFoundException {
//创建ObjectInputStream 对象 反序列化
ObjectInputStream objectInputStream=new ObjectInputStream(new FileInputStream("D:\\data.dat"));
//读取:反序列化顺序必须与序列化的顺序一致(什么顺序保存的就什么顺序读出)
System.out.println(objectInputStream.readInt());
System.out.println(objectInputStream.readChar());
System.out.println(objectInputStream.readUTF());
System.out.println(objectInputStream.readBoolean());
System.out.println(objectInputStream.readDouble());
//读Dog对象 注意必须要有Dog类才能调用Dog方法
Object o = objectInputStream.readObject();
System.out.println(o.toString());
Dog dog= (Dog) o;
System.out.println(dog.getName());
}
}
注意事项:
- 读写顺序要一致
- 要求实现序列化和反序列化对象,需要实现Serializable
- 序列化的类中建议添加SerialVersionUID,为了提高版本的兼容性
- 序列化对象时,默认将里面所有属性都进行序列化,除static和transient修饰的成员
- 序列化对象时,要求里面属性的类型也需要实现序列化接口
- 序列化具备可继承性,也就是如果某类实现了序列化,则其所有子类也默认实现了序列化
处理纯文本数据时,使用字符流效率更高,且可以有效解决中文乱码
可以在使用时指定编码格式(utf-8、gbk 等)
InputStreamReader
Reader的子类,可以将InputStream(字节流)转换(包装)成Reader(字符流)
//将 FileInputStream 转换成 InputStreamReader 并指定编码
InputStreamReader inputStreamReader=new InputStreamReader(new FileInputStream("D:\\data.dat"),"utf-8");
//将 InputStreamReader 传入BufferedReader
BufferedReader bufferedReader=new BufferedReader(inputStreamReader);
//读取
String str=bufferedReader.readLine();
//关闭资源(只需关闭外层)
bufferedReader.close();
OutputStreamWriter
Writer的子类,实现将OutputStream(字节流)转换(包装)成Writer(字符流)
//将 FileOutputStream 转换成 OutputStreamWriter 并指定编码
OutputStreamWriter outputStreamWriter = new OutputStreamWriter(new FileOutputStream("D:\\data.dat"), "utf-8");
//将 OutputStreamWriter 传入 BufferedWriter
BufferedWriter bufferedWriter = new BufferedWriter(outputStreamWriter);
//io写入
bufferedWriter.write(100);
//关闭资源(只需关闭外层)
bufferedWriter.close();
配置文件
# classfullpath 表类的全限定名;methood 表类方法
classfullpath=com.wang.Cat
methood=hi
读配置文件
//Properties类可以读写配置文件
Properties properties=new Properties();
//读
properties.load(new FileInputStream("src\\re.properties"));
//获取
String classfullpath=properties.get("classfullpath").toString();
System.out.println(classfullpath);
写配置文件
五类互联网网络地址
TCP/IP分层
数据进入协议栈时的封装过程 当成为以太网帧就可以通过物理层传输,对方获取以太网帧后会反向解码
TCP/IP分层:
![]()
常用方法 都需要抛出异常
获取本机InetAddress对象 — getLocalHost
获取指定主机名/域名获取Ip地址对象 — getByName
获取InetAddress对象的主机名 —getHostName
获取InetAddress对象的地址 —getHostAddress
//获取本机InetAddress对象
InetAddress inetAddress=InetAddress.getLocalHost();
System.out.println(inetAddress); //DESKTOP-NAQRCQR/192.168.31.1
//指定主机名获取InetAddress对象
InetAddress inetAddress=InetAddress.getByName("DESKTOP-NAQRCQR");
System.out.println(inetAddress); //DESKTOP-NAQRCQR/192.168.31.1
//指定域名获取InetAddress对象
InetAddress inetAddress=InetAddress.getByName("www.baidu.com");
System.out.println(inetAddress); //www.baidu.com/180.101.50.188
// 通过InetAddress对象 获取InetAddress对象的主机名和地址
InetAddress inetAddress=InetAddress.getByName("www.baidu.com");
String hostName=inetAddress.getHostName();
System.out.println(hostName); //www.baidu.com
String address=inetAddress.getHostAddress();
System.out.println(address); //14.119.104.189
Socket–英语解释:插孔
示意图
客户端
public class SocketTcpClient {
public static void main(String[] args) throws IOException {
//连接服务端(ip,端口)这里因为服务器是本地模拟,所以ip为本机的host.连接成功返回Socket
Socket socket=new Socket("192.168.8.107",9999);
//通过socket.getOutputStream 得到和socket对象关联的输出流对象
OutputStream outputStream = socket.getOutputStream();
//通过输出流,写入数据到数据通道
outputStream.write("hello Server".getBytes());
//设置输出结束标记
socket.shutdownOutput();
//通过socket.getInputStream 接受服务器返回的数据
InputStream inputStream = socket.getInputStream();
//IO读取
byte buf[]=new byte[1024];
int readLen=0;
while ((readLen=inputStream.read(buf))!=-1){
System.out.println(new String(buf,0,readLen));
}
//关闭资源(必须)
outputStream.close();
socket.close();
}
}
服务器
public class ScoketTcpServer {
public static void main(String[] args) throws IOException {
//在本机9999端口监听,等待客户端连接
ServerSocket serverSocket=new ServerSocket(9999);
//当没有客户端连接9999端口时,程序会阻塞,等待连接
System.out.println("服务端正在等待连接......");
//当有客户端连接,则返回Socket对象,程序继续执行
Socket socket=serverSocket.accept();
//通过socket.getInputStream 读取客户端写入数据通道的数据
InputStream inputStream = socket.getInputStream();
//IO读取
byte buf[]=new byte[1024];
int readLen=0;
while ((readLen=inputStream.read(buf))!=-1){
System.out.println(new String(buf,0,readLen));
}
//通过socket.getOutputStream 得到和socket对象关联的输出流对象
OutputStream outputStream = socket.getOutputStream();
//通过输出流,写入数据到数据通道
outputStream.write("hello Client".getBytes());
//设置输出结束标记(非常重要,不设置结束标记,就无法判断输出释放结束,程序会等待)
socket.shutdownOutput();
//关闭服务
inputStream.close();
socket.close();
serverSocket.close();
}
}
要求:
思路:
OutputStreamWriter InputStreamReader
4.设置写入结束标记(换行符,表写入结束) writer.newLine
客户端 只需要修改输出流的核心代码
//通过socket.getOutputStream 得到和socket对象关联的输出流对象
OutputStream outputStream = socket.getOutputStream();
//通过输出流,写入数据到数据通道 OutputStreamWriter将OutputStream的字符流转换为字节流
BufferedWriter bufferedWriter=new BufferedWriter(new OutputStreamWriter(outputStream));
bufferedWriter.write("hello,server");
//设置结束标记(插入一个换行符表写入结束) 对方必须使用readline读入
bufferedWriter.newLine();
//使用字符流需要手动刷新,否则数据不会写入到数据通道
bufferedWriter.flush();
服务端 只需要修改输入流的核心代码
//通过socket.getInputStream 读取客户端写入数据通道的数据
InputStream inputStream = socket.getInputStream();
//IO读取 通过InputStreamReader将inputStream转换为字符流
BufferedReader bufferedReader=new BufferedReader(new InputStreamReader(inputStream));
//读出
String str=bufferedReader.readLine();
System.out.println(str);
结构图:
客户端代码
public class SocketTcpClient {
public static void main(String[] args) throws IOException {
//连接服务端(ip,端口)这里因为服务器是本地模拟,所以ip为本机的host.连接成功返回Socket
Socket socket = new Socket("192.168.8.107", 9999);
//读取本地文件
FileInputStream fileInputStream = new FileInputStream("D:\\abc.jpg");
byte[] bytes = streamToByteArray(fileInputStream);
//发送到服务器
//通过socket.getOutputStream 得到和socket对象关联的输出流对象
OutputStream outputStream = socket.getOutputStream();
//通过输出流,写入数据到数据通道
outputStream.write(bytes);
//结束
socket.shutdownOutput();
//获取服务器返回的数据
InputStream inputStream = socket.getInputStream();
String s = streamToString(inputStream);
System.out.println(s);
//关闭资源(必须)
fileInputStream.close();
outputStream.close();
inputStream.close();
socket.close();
}
/**
* 功能:将输入流转换为byte[] ,即可以把文件的内容读到byte[]
*
* @param is
* @return
*/
public static byte[] streamToByteArray(InputStream is) throws IOException {
//创建输出流对象
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
//IO读取
byte[] b = new byte[1024];
int len;
while ((len = is.read(b)) != -1) {
byteArrayOutputStream.write(b, 0, len);
}
//转换为数组
byte[] array = byteArrayOutputStream.toByteArray();
//关闭资源
byteArrayOutputStream.close();
return array;
}
/**
* 功能:将输入流转换成String ,即把读到的文件内容以String形式返回
*
* @param is
* @return
*/
public static String streamToString(InputStream is) throws IOException {
//IO读取 通过InputStreamReader将inputStream转换为字符流
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(is));
//读出
String str = bufferedReader.readLine();
return str;
}
}
服务器代码
public class ScoketTcpServer {
public static void main(String[] args) throws IOException {
//在本机9999端口监听,等待客户端连接
ServerSocket serverSocket = new ServerSocket(9999);
//当没有客户端连接9999端口时,程序会阻塞,等待连接
System.out.println("服务端正在等待连接......");
//当有客户端连接,则返回Socket对象,程序继续执行
Socket socket = serverSocket.accept();
//读取客户端写入数据通道的数据
InputStream inputStream = socket.getInputStream();
//Io读出
byte[] bytes = streamToByteArray(inputStream);
//将数据写入到指定路径
FileOutputStream fileOutputStream = new FileOutputStream("./src/abc.jpg");
fileOutputStream.write(bytes);
//向客户端回复信息
OutputStream outputStreamString = socket.getOutputStream();
BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(outputStreamString));
bufferedWriter.write("收到图片");
//将内容刷新到数据通道,并设置写入结束
bufferedWriter.flush();
socket.shutdownOutput();
//关闭服务
fileOutputStream.close();
inputStream.close();
bufferedWriter.close();
outputStreamString.close();
socket.close();
serverSocket.close();
}
/**
* 功能:将输入流转换为byte[] ,即可以把文件的内容读到byte[]
*
* @param is
* @return
*/
public static byte[] streamToByteArray(InputStream is) throws IOException {
//创建输出流对象
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
//IO读取
byte[] b = new byte[1024];
int len;
while ((len = is.read(b)) != -1) {
byteArrayOutputStream.write(b, 0, len);
}
//转换为数组
byte[] array = byteArrayOutputStream.toByteArray();
//关闭资源
byteArrayOutputStream.close();
return array;
}
/**
* 功能:将输入流转换成String ,即把读到的文件内容以String形式返回
*
* @param is
* @return
*/
public static String streamToString(InputStream is) throws IOException {
//IO读取 通过InputStreamReader将inputStream转换为字符流
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(is));
//读出
String str = bufferedReader.readLine();
return str;
}
}
当客户端连接到服务端后,实际上客户端也是通过一个端口和服务端进行通讯的,这个端口是随机的,由TCP/IP来分配。
示意图:
程序连接时查询端口信息netstat:
反射是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为 Java 语言的反射机制
实例:根据配置文件 re.properties 指定信息,创建Cat对象并调用方法hi
# classfullpath 表类的全限定名;methood 表类方法
classfullpath=com.wang.Cat
methood=hi
这样的需求在学习框架的时候特别多,即通过外部文件配置,在不修改源代码的情况下,来控制程序,符合ocp开闭原则
开-功能扩展 ;闭-源码修改功能关闭
以上功能使用反射机制实现
//读取配置文件
Properties properties = new Properties();
properties.load(new FileInputStream("src\\re.properties"));
String classfullpath = properties.get("classfullpath").toString(); //类全限定名
String methoodName = properties.get("methood").toString(); //类方法名
//使用反射机制
//(1)加载类,返回Class类型的对象 cls
Class cls = Class.forName(classfullpath);
//(2)通过cls得到你加载的类 com.wang.Cat
Object obj = cls.newInstance();
//(3)通过cls得到你加载的类 com.wang.Cat 的method方法对象
Method method = cls.getMethod(methoodName);
//(4)通过method方法对象 实现调用方法
method.invoke(obj);//传统方法 对象.方法() ==>反射机制 方法.invoke(对象)
反射(Reflection) 允许程序在执行期间借助于ReflectionAPI取得任何类的内部信息;
加载完类后,在堆中会产生一个Class类型的对象(一个类只有一个Class对象),这个对象包含类的完整结构信息,通过这个对象得到类的结构。这个对象就像一面镜子,透过这个镜子看到类的结构,所以形象称之为反射。
原理图
java.lang.Class //代表一个类,Class对象表示某个类加载后在堆中的对象
java.lang.reflect.Method //代表类的方法
java.lang.reflect.Field //代表类的成员变量
java.lang.reflect.Constructor //代表类的构造方法
演示:
//(1)加载类,返回Class类型的对象 cls
Class cls = Class.forName("com.wang.Cat");
//(2)通过cls得到你加载的类 com.wang.Cat
Object obj = cls.newInstance();
//(3)通过cls得到你加载的类 com.wang.Cat 的method方法对象
Method method = cls.getMethod("hi");
//(4)通过method方法对象 实现调用方法
method.invoke(obj);//传统方法 对象.方法() ==>反射机制 方法.invoke(对象)
//(5)通过cls得到你加载的类的Field成员变量对象 得到公有成员变量字段 注意,.getField不能得到私有属性
Field ageField = cls.getField("age");
System.out.println(ageField.get(obj).toString()); //==>反射 属性.get(对象)
//(6)通过cls得到你加载的类的 Constructor 构造方法对象,Constructor表构造器
//无参构造
Constructor constructor = cls.getConstructor(); //()中可以指定构造器参数类型,返回无参构造
System.out.println(constructor);
//有参构造
Constructor constructor1 = cls.getConstructor(String.class); //指定构造器参数类型 形参类型.class
System.out.println(constructor1);
优点:可以动态创建和使用对象(也是框架的底层核心),使用灵活,没有反射机制,框架技术就失去了底层支撑
缺点:使用反射基本是解释执行,对执行速度有影响
优化-关闭访问检查
Method和Field、Constructor对象都有setAccessible()方法
setAccessible方法作用是启动和禁用访问安全检查的开关
参数值为true表示反射的对象在使用时取消访问检查,提高反射效率
参数值为f’alse表示反射的对象执行访问检查
//(1)加载类,返回Class类型的对象 cls
Class cls = Class.forName("com.wang.Cat");
//(2)通过cls得到你加载的类 com.wang.Cat
Object obj = cls.newInstance();
//(3)通过cls得到你加载的类 com.wang.Cat 的method方法对象
Method method = cls.getMethod("hi");
//(4)反射优化
method.setAccessible(true);
//(5)通过method方法对象 实现调用方法
method.invoke(obj);//传统方法 对象.方法() ==>反射机制 方法.invoke(对象)
有Clss对象的类型
Class类常用方法:
//获取到Car类对应的Class对象 >表示不确定的java类型
Class<?> cls = Class.forName("com.wang.Cat");
//输出cls 显示cls对象是哪个类的Class对象(不是具体类,不能强转)
System.out.println(cls); //com.wang.Cat
//输出cls运行类型
System.out.println(cls.getClass()); //java.lang.Class
//得到包名
System.out.println(cls.getPackage().getName()); //com.wang
//得到全类名 (全限定名)
System.out.println(cls.getName()); //com.wang.Cat
//通过cls创建对象实例 输出信息
Cat cat = (Cat) cls.newInstance();
System.out.println(cat); //等价cat.toString() 结果 Cat{name='cat', age='10'}
//通过反射获取某个属性(仅公有)
Field age = cls.getField("age"); //括号内只能填公有属性名 “公有属性名” 私有报错
System.out.println(age.get(cat)); //10
//通过反射获取所有属性(仅公有)
Field[] fields = cls.getFields();
for (Field field : fields) {
System.out.println(field.get(cat));
}
//通过反射给属性赋值
age.set(cat, "20");
System.out.println(age.get(cat)); //20
方式一:
前提:若已知一个类的全类名,且该类在路径下,可通过Class类的静态方法forName()获取
场景:多用于配置文件,读取类全路径,加载类
Class<?> cls = Class.forName("com.wang.Cat");
方式二:
前提:若已知具体的类,通过类的Class获取,该方式最为安全可靠,程序性能最高
场景:多用于参数传递,比如通过反射得到对应构造器对象
Class cls = Cat.class;
方式三:
前提:若已知某个类的实例,调用该实例的getClass()方式获取Class对象
场景:通过创建好的对象,获取Class对象
Class cls = cat.getClass();
方式四:
前提:
场景:通过类加载器来获取类的Class对象
//先得到类加载器
ClassLoader classLoader = cat.getClass().getClassLoader();
//通过类加载器得到Class对象
Class cls = classLoader.loadClass("com.wang.Cat");
方式五:
前提:基本数据类型
场景:基本数据类型获取Class对象
//Class cls=基本数据类型.Class;
Class cls = int.class;
方式六:
前提:包装类
场景:包装类获取Class对象
//Class cls=包装类.TYPE;
Class cls = Integer.TYPE;
反射机制是java实现动态语言的关键,也就是通过反射实现类动态加载
静态加载:编译时加载相关的类,如果没有则报错,依赖性强
动态加载:运行时加载需要的类,如果运行时不用该类,则不报错,降低依赖性
类加载流程图
类加载三个阶段完成的任务
验证:对文件安全进行效验;
准备:对静态变量进行默认初始化,并分配内存空间;
解析:将符号引用转换成直接引用;
加载阶段
JVM在该阶段主要目的是将字节码从不同数据源(可能是class文件、也可能是jar包、甚至网络)转化为二进制字节流加载到内存中,并生成一个代表该类的java.lang.Class对象。
连接阶段-验证
- 目的是为了确保Class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身安全。
- 包括:文件格式验证(是否以魔数oxcafebabe开头)、元数据验证、字节码验证和符号引用验证
- 可以考虑使用 -Xverify:none参数来关闭大部分类验证措施,缩短虚拟机类加载时间
连接阶段-准备
JVM会在该阶段对静态变量,分配内存并默认初始化(对应数据类型的默认初始值,如0、0L、null、false等)。这些变量所使用的n内存都将在方法区进行分配。
//n1是实例属性,不是静态变量,因此在准备阶段,不会分配内存 //n2是静态变量,准备阶段分配内存,n2默认初始化0; 想获得20这值要在类加载最后阶段初始化中获得 //n3是static final 是常量,一旦赋值不可变,准备阶段n3值就是30 public int n1=10; public static int n2=20; public static final int n3=30;
连接阶段-解析
虚拟机将常量池内的符号引用替换为直接引用的过程
初始化
- 到初始化阶段,才真正开始执行类中定义的Java程序代码,此阶段是执行()方法的过程。
- ()方法是由编译器按语句在源文件中出现的顺序,依次自动收集类中所有静态变量的赋值动作和静态代码块中的语句 ,并进行合并
- 虚拟机会保证一个类的()方法在多线程环境中被正确加锁、同步,如果多个线程同时区初始化一个类,那么只有一个线程会去执行这个类的()方法,其他线程都需要阻塞等待,直到活动线程执行()方法完毕
class B{ static { System.out.println("B静态代码块"); num=10; } static int num=20; } //分析 //1.加载阶段:加载B类,并生成B的class对象 //2.连接阶段:num=0 //3.初始化阶段:依次自动收集类中的所有静态变量的赋值动作和静态代码块中语句,并合并 注意是按顺序收集合并 /* * clinit(){ * System.out.println("B静态代码块"); * num=10; * num=10; * * } */
Cat类
public class Cat implements i1 {
public String name="cat";
String age="10";
protected String height="100cm";
private String weight="30kg";
public void m1(){}
void m2(){}
protected void m3(){}
private void m4(){}
}
测试类
public class todo {
public static void main(String[] args) throws Exception {
//获取到Car类对应的Class对象 >表示不确定的java类型
Class<?> cls = Class.forName("com.wang.Cat");
//getName() 获取全类名
System.out.println(cls.getName()); //com.wang.Cat
//getSimpleName() 获取简单类名
System.out.println(cls.getSimpleName()); //Cat
//getFields() 获取所有public修饰的属性,包含本类及父类
Field[] fields = cls.getFields();
for (Field field : fields) {
System.out.println(field.getName()); //name
}
//getDeclaredFields() 获取本类中所有属性
Field[] declaredField = cls.getDeclaredFields();
for (Field field : declaredField) {
System.out.print(field.getName() + " ");//name age height weight
}
System.out.println();
//getMethods() 获取所有public修饰的方法 ,包含本类及父类
Method[] methods = cls.getMethods();
for (Method method : methods) {
System.out.print(method.getName() + " "); //m1 wait wait wait equals toString hashCode getClass notify notifyAll
}
System.out.println();
//getDeclaredMethods() 获取本类所有方法
Method[] declaredMethods = cls.getDeclaredMethods();
for (Method declaredMethod : declaredMethods) {
System.out.print(declaredMethod.getName() + " "); //m2 m1 m3 m4
}
System.out.println();
//getConstructors() 获取所有public修饰的构造器,包含本类
Constructor<?>[] constructors = cls.getConstructors();
for (Constructor<?> constructor : constructors) {
System.out.print(constructor.getName()); //com.wang.Cat
}
System.out.println();
//getPackage() 返回包类型
System.out.println(cls.getPackage()); //package com.wang
//getSuperclass() 以class形式返回父类信息(父类的class对象)
Class<?> superclass = cls.getSuperclass();
System.out.println(superclass); //class java.lang.Object
//getInterfaces() 以class形式返回接口信息
Class<?>[] interfaces = cls.getInterfaces();
for (Class<?> anInterface : interfaces) {
System.out.print(anInterface + " ");//interface com.wang.i1
}
System.out.println();
//getAnnotations() 返回注解信息
Annotation[] annotations = cls.getAnnotations();
for (Annotation annotation : annotations) {
System.out.print(annotation + " ");
}
方式一:调用类中的public修饰的无参构造器
方式二:调用类中的指定构造器
Class类相关方法
newInstance: 调用类中无参构造器,获取对应类的对象
getConstructor(Class…class):根据参数列表,获取对应的构造器对象
getDecalaredConstructor(Class…class):根据参数列表,获取对应的所有构造器对象
Constructor类相关方法
setAccessible:暴破
newInstance(Object…obj):调用构造器
实例一:通过反射创建某类对象,分别通过公有无参、公有有参、私有有参构造
/**
* Cat类
*/
public class Cat {
private String name = "cat";
private String age = "10";
public Cat() {
}
public Cat(String name) {
this.name = name;
}
private Cat(String name, String age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Cat{" +
"name='" + name + '\'' +
", age='" + age + '\'' +
'}';
}
}
/**
* 测试类
*/
public class todo {
public static void main(String[] args) throws Exception {
//获取到Car类对应的Class对象 >表示不确定的java类型
Class<?> cls = Class.forName("com.wang.Cat");
//通过public无参构造器创建实例
Object o = cls.newInstance();
System.out.println(o); //Cat{name='cat', age='10'}
//通过public有参构造器创建实例 getConstructor返回一个构造器
Constructor<?> constructor = cls.getConstructor(String.class);
//通过构造器创建实例
Object dog = constructor.newInstance("Dog");
System.out.println(dog); //Cat{name='Dog', age='10'}
//通过private有参构造器创建实例 私有构造器需要通过getDeclaredConstructor返回构造器
Constructor<?> declaredConstructor = cls.getDeclaredConstructor(String.class, String.class);
//暴破,若不添加则报错,私有不能直接创建实例。 【暴力破解】,使用反射可以访问私有构造器/属性/方法
declaredConstructor.setAccessible(true);
//创建实例
Object pig = declaredConstructor.newInstance("Pig", "30");
System.out.println(pig);//Cat{name='Pig', age='30'}
实例一:反射操作属性
//获取到Car类对应的Class对象 >表示不确定的java类型
Class<?> cls = Class.forName("com.wang.Cat");
//创建对象
Object obj = cls.newInstance();
//使用反射得到public name对象 getField可以拿到公有属性
Field name = cls.getField("name");
name.set(obj,"Dog");
System.out.println(obj);//Cat{name='Dog', age='10'}
//使用反射得到privaet age对象 getDeclaredField可以拿到任意权限属性
Field age = cls.getDeclaredField("age");
//暴破 私有属性不能直接操作,必须爆破
age.setAccessible(true);
age.set(obj,"30");
System.out.println(obj);//Cat{name='Dog', age='30'}
//获取属性值
System.out.println(name.get(obj));//Dog
System.out.println(age.get(obj));//30
根据方法名和参数列表获取Method方法对象: Method method = class对象.getDeclaredMethod(“方法名”,参数.class);
获取对象: Object obj = class对象.newInstance();
暴破: method.setAccessible(true);
访问:Object invoke = method.invoke(obj,实参列表);
注意:如果是静态方法,则invoke()的参数obj可以写为null
//获取到Car类对应的Class对象 >表示不确定的java类型
Class<?> cls = Class.forName("com.wang.Cat");
//获取对象
Object obj = cls.newInstance();
//根据方法名和参数列表获取Method方法对象 getMethod获取公有方法 m1公有
Method methodM1 = cls.getMethod("m1",String.class);
//访问
Object invoke = methodM1.invoke(obj,"Dog");
System.out.println(obj); //Cat{name='Dog', age='10'}
//根据方法名和参数列表获取Method方法对象 getDeclaredMethod获取所有方法 m2私有
Method methodM2 = cls.getDeclaredMethod("m2", String.class);
//私有方法需要暴破
methodM2.setAccessible(true);
//访问
Object invoke1 = methodM2.invoke(obj, "30");
System.out.println(obj); //Cat{name='Dog', age='30'}