1-Java基础笔记

字符串函数

获取字符串长度 lenght

返回类型:int

作用:获取字符串长度

用法: int a=b.lenght();

获取字符 charAt

返回类型:String

作用:获取字符串内某一下标对应的值

用法:char a=b.charAt(下标);

字符串相同比较 equals

equals(“被比较的字符串”);

返回类型:boolean

作用:判断两字符串是否相同

用法:boolean bool=str.equals("java");

字符串大小比较 CompareTo

返回类型:int ascill差值 大正同零小负

作用:判断两字符串大小关系,按位依次比较

用法:int ct=str.CompareTo("java");

字符串输出 toString

返回类型:String

作用:将当前对象以字符串的形式返回

用法:System.out.println(date.toString());

去除字符 trim

返回类型:String

作用:移除字符串两侧的空白字符或其他预定义字符

用法:String newa=a.trim(); //去除字符串a左右的空格

查找字符 indexof

返回类型: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

substring(开始索引,结束索引);

返回类型:String 包含开始索引,不包含结束索引 无结束索引即默认打印开始索引后的全部

作用:返回当前字符串中一个连续的片段

用法1:String newStr = str.substring(4);

用法2:String newStr = str.substring(4,7);

字符串开始内容判断 startsWith

返回类型:boolean

作用:检查给定的字符串是否以指定字符串的字符开头

用法:boolean bool=url.startsWith("字符串");

字符串结束内容判断 endsWith

返回类型:boolean

作用:检查给定的字符串是否以指定字符串的字符结尾

用法: boolean bool=url.endsWith("字符串");

字符串替换 replaceAll

replaceAll(“要替换的值”,“新值”)

返回类型:String 失败返回原字符串

作用:在字符串中用一些字符替换另一些字符,只返回新字符串,原字符串不变

用法: String newStr = str.replaceAll("Java","Python");

字符串分割 split

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;

JSON序列化与反序列化

JSON对象序列化

基本格式:

1.必须是对象{}或数组【】

语法规则:

img

值类型:

img

值可以是数组对象,就意味着数据和对象可以任意嵌套、任意深度

如果需要处理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<User>集合
    for (User user : users) {
        System.out.println(user.getUserName());
    }
} catch (Exception e) {
    e.printStackTrace();
}

将JSON文件读取到集合

使用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文件,在节点里配置如下内容:

<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.10.0</version>
</dependency>

注:是配置依赖的

文件读

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

Vaadin

输入框组件TextTield

声明:com.vaadin.flow.component.textfield.TextField

实例化:TextField field = new TextField();

添加用布局组件,add可以添加任意子组件: add(field);

布局组件 OrderdLayout

垂直布局声明: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);//添加窗口
    }
}

button按钮

声明:com.vaadin.flow.component.button.Button

创建一个保存按钮对象并添加信息:Button addButton = new Button(“提示信息”);

添加提示信息也可以这样:addButton.setText(“提示信息”);

添加:add(addButton)

Lambda闭包

给 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("用户名正确");
    }

});

Notification界面提示

声明: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(“”)

支持多界面,“”中填写地址

@Route("/reg.html")

包装类

image-20230603195750164

包装类和基本类型的转换

装箱

基本类型—>包装类型

拆箱

包装类型—>基本类型


手动装箱

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); //自动装箱

面试题:Intefer创建机制

下列输出结果?

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详解

image-20230604140957296

  • String实现了Serializable接口 可以串行化 -即可以在网络传输

  • String被final修饰,不能被改变(地址而不是字符内容,每次修改都是产生一个新的String)

  • String 有属性private final byte[] value; 用于存放字符串内容

    String面试题

    题一:判断下面代码输出结果

    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    指向同一个常量池字符串  (地址相同)
    

    内存图

    image-20230604153029850


    题二:以下语句创建了几个对象

    String str="he";
    str="hehe";   //两个 每次修改都会创建新的String 对象的引用改变了
    

    内存图

    image-20230604153636599


题三:创建了几个对象

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"
*/

内存图

image-20230604155013024

注意:

  • String c=”hello“+”abc";常量相加 看的是池
  • String c=a+b; 变量相加 是在堆中

StringBuffer

创建方法

//创建一个大小为16的 char[] ,用于存放字符内容
StringBuffer stringBuffer1=new StringBuffer();

//通过构造器指定 char[] 大小
 StringBuffer stringBuffer2=new StringBuffer(50);

//通过给一个Strintg 创建StringBuffer  char[] 大小就是 String.length()+16
 StringBuffer stringBuffer3=new StringBuffer("hello");

StringBuffer转换

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 stringBuffer=new StringBuffer("hello");

stringBuffer.append(" ");
stringBuffer.append("world").append(10).append(true);
System.out.println(stringBuffer);  //结果:hello world10true    

stringBuffer.delete(0,5); //删除索引为>=start && <end处的字符(0-4)

改(替换)

stringBuffer.replace(0,5,"你好");  //替换0-4处字符为你好

查(查找指定子串第一次出现的索引)

int indexOf = stringBuffer.indexOf("world");

插入(在指定位置插入,原位置字符自动后移)

stringBuffer.insert(0,"hello"); 

StringBuffer面试题

题目一:将空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;
    }

StringBuilder

和StringBuffer用法完全一致

数组

Arrays

toString遍历

可以使用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排序

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会影响排序的结果


Arrays.binarySearch二叉查找

前提:数组有序

Integer[] arr={1, 2, 3, 4};

int i1 = Arrays.binarySearch(arr, 3);
System.out.println(i1);

Arrays.copyOf 拷贝

从arr数组中,拷贝arr.length个元素到arr1

如果拷贝长度大于arr.length 就在新数组后面加null 拷贝长度小于零报异常

Integer[] arr={1, 2, 3, 4,-5,-3,0};
        
Integer[] arr1=Arrays.copyOf(arr,arr.length);

Arrays.fill 数组元素替换

使用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 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);

将格式化的字符串转换成Data

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

第三代日期时间LocalDateTime

日期: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

返回当前时间 now

用法: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);  //当天

集合

image-20230609101530653 image-20230609101604149

集合分为单列集合Collection 和双列集合Map


Collection接口遍历元素-迭代器

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'}

Collection接口遍历元素-增强for循环

增强for循环可以代替Iterator迭代器

增强for循环就是简化的Iterator(其底层就是Iterator),本质一样,只能用于遍历集合和数组.

for (Object o : list) {
    System.out.println(o);
}

List

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

Set无序不可重复,最多允许一个null对象,无索引,只能使用迭代器取得所有元素,逐一遍历

插入多个null不会报错,只是加入失败

Set有两个子类 HashSet TreeSet

HashSet

对象加入hashSet时,hashSet会先计算对象的hashCode值判断对象加入的位置,如果发现有值,则调用equals方法来检测两个对象是否相同,如果相同就加入失败,如果不同,就散列到其他位置,这样减少了equals的次数,提高了执行速度

  • 如果两个对象相等,则hashCode一定相同,反之不成立
  • qeuals方法被重写,则hashCode方法也必须重写
  • hashCode的值会转化为索引值

–则 未重写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用于保存具有映射关系的数据

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<Entry<k,v>> 即:transient Set<Map.Entry<k,v>> 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

泛型

泛型,即“参数化类型”。

泛型的作用:在类声明时通过一个标识表示类中某个属性的类型以及方法的返回类型

  • 代码更加简洁【不用强制转换】
  • 程序更加健壮【只要编译时期没有警告,那么运行时就不会抛出ClassCastException的异常】
  • 可读性和稳定性【在编写集合的时候,就限定了类型】

​ 往集合添加数据并遍历时

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>();

java绘图

Component类提供两个和绘图相关最重要的方法:

  • paint(Graphics g) 绘制组件的外观
  • repaint() 刷新组件的外观

画圆案例

先定义一个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类绘制方法

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)

实例-绘制坦克

image-20230610191646363

java事件处理机制

演示小球通过键盘上下左右控制移动轨迹

先定义一个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()


IO 文件

文件的创建

创建文件对象相关方法

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("目录删除失败");
            }
        }

IO原理及其分类

I/O 是Input/Output的缩写 用于处理数据传输

Java程序中,对于数据的输入输出操作以 流(stream)的方式进行

java.io包下提供各种“流”类和接口,用于获取不同种类的数据,并通过方法输入或输出数据


输入输出

输入input:读取外部数据(磁盘)到程序(内存)

输出output:将程序(内存)数据输出到磁盘


流的分类

按操作数据单位不同分为:字节流(8 bit 二进制文件)字符流(按字符 文本文件)

按数据流的流向不同分为:输入流、输出流

按流的角色不同分为:节点流、处理流/包装流

抽象基类字节流字符流
输入流InputStreamReader
输出流OutputStreamWriter

Java的IO流共涉及40多个类,实际上非常规则,都是从如上4个抽象基类派生的

由这四个类派生出来的子类名称都是以其父类名作为子类后缀名


字节输入输出流

InputStream抽象类是所有类字节输入流的超类

InputStream常用的子类:

  1. FileInputStream 文件输入流
  2. BufferedInputStream 缓冲字节输入流
  3. ObjectInputStream 对象字节输入流

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);
            }
        }
    }

节点流与处理流

  • 节点流可以从一个特定的数据源读写数据,如FileReader、FileWriter
  • 处理流(也叫包装流)是连接已存在的流(节点流或处理流)之上,为程序提供更为强大的读写能力,如BufferedReaderBufferedWriter

image-20230707143425481

节点流与处理流的区别与联系:

节点流是底层流(低级流),可以直接跟数据源连接,处理流包装节点流使用了修饰器设计模式,不直接与数据源连接

处理流包装节点流,既可以消除不同节点流的实现差异,也可以提供更方便的方法来完成输入输出

处理流的功能:

性能提高 --主要以增加缓冲的方式提高输入输出效率

操作便捷 --处理流提供一系列便捷方法来一次输入输出大批量数据


处理流字符输入输出

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形式读出。就是能将基本数据类型或对象进行序列化和反序列化操作

序列化和反序列化

  1. 序列化就是在保存数据时,保存数据的值和数据类型
  2. 反序列化就是在恢复数据时,恢复数据的值和数据类型
  3. 需要让某个对象支持序列化机制,必须让其类是可序列化的,为了让某个类是可序列化的,该类必须实现两个接口之一: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());
    }
}

注意事项:

  1. 读写顺序要一致
  2. 要求实现序列化和反序列化对象,需要实现Serializable
  3. 序列化的类中建议添加SerialVersionUID,为了提高版本的兼容性
  4. 序列化对象时,默认将里面所有属性都进行序列化,除static和transient修饰的成员
  5. 序列化对象时,要求里面属性的类型也需要实现序列化接口
  6. 序列化具备可继承性,也就是如果某类实现了序列化,则其所有子类也默认实现了序列化

转换流

处理纯文本数据时,使用字符流效率更高,且可以有效解决中文乱码

可以在使用时指定编码格式(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);

写配置文件


网络

网络协议

五类互联网网络地址

image-20230706095823352


TCP/IP分层

image-20230706100001059

数据进入协议栈时的封装过程 当成为以太网帧就可以通过物理层传输,对方获取以太网帧后会反向解码

TCP/IP分层:

image-20230706100334171

InetAddress

常用方法 都需要抛出异常

获取本机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

Socket–英语解释:插孔

  • 套接字(Socket)开发网络应用程序被广泛采纳,成为事实上的标准。
  • 通信两端都要有Socket,是两台机器通信的端点
  • 网络通信实际上就是Socket间的通信
  • Socket允许程序把网络连接当成一个流,数据在两个Socket间通过IO传输
  • 一般主动发起通信的应用程序属于客户端,等待通信请求为服务端

示意图

image-20230706154019064

TCP字节流通信

  1. 编写一个服务端,和客户端
  2. 服务端在9999端口监听
  3. 客户端连接服务端,发送数据,并接收服务端返回的数据,再退出
  4. 服务端接收到客户端发出的数据,然后返回自己的数据,再退出

客户端

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();
    }
}

TCP字符流通信

要求:

  1. 编写一个服务端,和客户端
  2. 服务端在9999端口监听
  3. 客户端连接服务端,发送数据,并接收服务端返回的数据,再退出
  4. 服务端接收到客户端发出的数据,然后返回自己的数据,再退出

思路:

  1. 客户端使用socket.getOutputStream(); 服务端使用socket.getInputStream();
  2. 客户端将OutputStream–>Writer 服务端InputStream ->Reader
  3. 客户端使用转换流 服务端使用转换流

​ 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);

网络上传文件到服务器

结构图:

image-20230712153340629

客户端代码

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连接的秘密

当客户端连接到服务端后,实际上客户端也是通过一个端口和服务端进行通讯的,这个端口是随机的,由TCP/IP来分配。

示意图:

image-20230713090450561

程序连接时查询端口信息netstat:

image-20230713091311895

反射

反射是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为 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对象),这个对象包含类的完整结构信息,通过这个对象得到类的结构。这个对象就像一面镜子,透过这个镜子看到类的结构,所以形象称之为反射。

原理图

image-20230721115856411

反射相关类

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(对象)

Class类

  1. Class也是类,因此也继承Object类
  2. Class类对象不是new出来的,而是系统创建的
  3. 对于某个类的Class类对象,在内存中只有一份,因为类只加载一次(被synchronized修饰)
  4. 每个类的实例都会记得自己是由哪个Class实例生成的
  5. 通过Class可以完整的得到一个类的完整结构,通过一系列API
  6. Class对象是存放在堆里的
  7. 类的字节码二进制数据,是放在方法堆区的,有的地方称为元数据(包括方法代码、变量名、方法名、访问权限等)

有Clss对象的类型

  1. 外部类,成员内部类,静态内部类,局部内部类,匿名内部类
  2. 接口 interface
  3. 数组 arrays
  4. 枚举 enum
  5. 注解 annotation
  6. 基本数据类型
  7. void

Class类常用方法:

image-20230724090051062
        //获取到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对象六种方式

方式一:

前提:若已知一个类的全类名,且该类在路径下,可通过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实现动态语言的关键,也就是通过反射实现类动态加载

静态加载:编译时加载相关的类,如果没有则报错,依赖性强

动态加载:运行时加载需要的类,如果运行时不用该类,则不报错,降低依赖性


类加载流程图

image-20230725090450121

类加载三个阶段完成的任务

  • 加载:将类的class文件读入内存,并为之创建一个java.lang.class对象。此过程由类加载器完成。
  • 连接:将类的二进制数据合并到JRE中。

验证:对文件安全进行效验;

准备:对静态变量进行默认初始化,并分配内存空间;

解析:将符号引用转换成直接引用;

  • 初始化:JVM负责对类进行初始化,这里主要是指静态成员。

类加载阶段

加载阶段

JVM在该阶段主要目的是将字节码从不同数据源(可能是class文件、也可能是jar包、甚至网络)转化为二进制字节流加载到内存中,并生成一个代表该类的java.lang.Class对象。

连接阶段-验证

  1. 目的是为了确保Class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身安全。
  2. 包括:文件格式验证(是否以魔数oxcafebabe开头)、元数据验证、字节码验证和符号引用验证
  3. 可以考虑使用 -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;
    *
    * }
    */

反射获取类结构信息

image-20230725111612715

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'}

反射暴破-操作属性

  • 根据属性名获取Field对象: Field field=class对象.getDeclardeField(属性名);
  • 暴破: field.setAccessible(true);
  • 访问: field.set(obj,值); System.out.println(field.get(obj));
  • 注意:如果是静态属性,则set的参数obj可以写null 即 field.set(null,值);

实例一:反射操作属性

        //获取到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'}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: 尚硅谷Java基础笔记是一套系统而全面的Java基础学习资料。这份笔记Java语言的基本概念介绍开始,涵盖了Java的核心知识点,包括变量、数据类型、运算符、流程控制等。接着,笔记详细讲解了面向对象编程的特点和Java中的类、对象、封装、继承、多态等内容。 在这份笔记中,还深入讨论了Java中的常用类库,如String、ArrayList、HashMap等,以及它们的常见用法和实例操作。此外,笔记还介绍了常见的异常处理机制,包括try-catch语句、throw和throws关键字等,帮助学习者理解并掌握Java中的错误和异常处理。 除了基础知识的讲解,尚硅谷Java基础笔记还提供了大量的例子和练习题,帮助学习者巩固所学内容,并通过实践提高编程能力。这些例子和练习题涵盖了各个知识点和应用场景,从简单到复杂,逐渐深入,非常有助于学习者的理解和应用能力的提升。 总而言之,尚硅谷Java基础笔记是一份详细、系统、易懂的学习资料,适合初学者入门学习Java编程。无论是对于零基础的学习者还是对于有一定编程经验的人来说,这份笔记都是一份不可多得的宝藏,可以帮助他们夯实Java基础,掌握编程技巧,为进一步扩展知识奠定坚实的基础。 ### 回答2: 尚硅谷java基础笔记是一份完整而详细的教程,对于初学者来说是一个很好的学习资源。 这份笔记由尚硅谷团队精心编写而成,包含了Java基础的各个面。首先从Java的历史和应用领域入手,引导读者对Java一个整体的认识。然后逐步介绍Java的发展环境和使用工具,让读者能够了解如何配置和使用Java开发环境。 接下来,笔记逐个介绍了Java的基本语法、数据类型、运算符、流程控制语句等基础知识。这些内容以简洁明了的式呈现,附有实例和练习题,让读者可以通过练习巩固所学内容。 除了基础语法,笔记还深入讲解了Java的面向对象编程思想和相关特性,包括类与对象、封装、继承、多态等。这部分内容对于理解Java的核心思想和编程范式非常重要,而且通过实例和案例的讲解,更加直观地展示了面向对象的优势和应用。 此外,笔记还包含了Java的常用类库的介绍,如集合框架、IO流、异常处理等,这些内容对于进行实际开发非常实用。 总之,尚硅谷java基础笔记内容全面、深入浅出,适合初学者学习和巩固基础知识。通过系统地学习这份笔记,读者可以建立起扎实的Java基础,为后续的学习和实践打下坚实的基础。同时,笔记中的案例和练习题也为读者提供了不少实践机会,帮助读者更好地理解和应用所学知识。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值