javase基础

Java是一门面向对象编程语言。

1、windows常用dos命令

        dir 查看目录下的文件

        cd 进入指定目录

        盘:切换盘

        md 创建文件夹

        rd 删除文件夹

        echo 打印

        more

        findstr语法 findstr + "字符" + 文件名 在指定文件中搜索指定字符的行 (字符可用正则)

        del 删除文件

        move 同mv

2、jdk的卸载

        控制面板卸载所有Java应用

3、常量

        1、字面值常量(就是数,如:1,2,3都是常量)

        2、自定义常量(被final关键字修饰的变量)

4、数据类型

1、基本数据类型(四大类)

        整型:byte, short, int, long

        浮点数:float, double

        字符:char

        布尔:true, false

    注:long 类型赋值后要加L(不超过int的范围会自动转换,不加L也许),float 类型赋值后要加f,char用单引号,string用双引号

2、引用数据类型

        String等(略)

3、自动类型转换:

        byte => int => long => float => double

        short => int => long => float => double

        char => int => long => float => double

4、强制数据转换:

        要转换的数据前面加上“(要转换的类型)”

5、阿斯克码值:

        ‘0’ 48, ‘A’ 65, ‘a’ 97。

5、运算符

1、算数运算符

        +, -, *, /, %, ++, --

注: + 运算顺序:从左往右,有字符串加表示连接,有char表示阿斯克码值相加

        s += 1 不等于 s = s + 1 等于 s = (s类型) (s + 1)

        计算机所有运算都是采用补码进行

2、逻辑运算符

        $, |, !, ^, $$, || (短路与$$ 短路或 || )

3、赋值运算符

        =, +=, -=, *=, /=, %=

4、关系运算符

        ==, !=, >, =,

5、位运算符

        &, |, ~(取反), ^, >(右移最高位是1就补1,是0就补0), >>>

注:左移n位就是乘以2的n次方,右移n位就是除以2的n次方

        二进制补码运算

6、三目运算符

(关系表达式)?(表达式1):(表达式2);

注:必须有返回值,返回值取决于两个表达式返回值类型最大的一个

7、进制转换

超详细进制转换(二进制、八进制、十进制、十六进制)详解_爱躺平的威威的博客-CSDN博客

6、选择语句

if语句

switch语句 注:default语句可以放在任意位置,没有break,default会继续执行后续语句

7、循环语句

        for, while, do...while

注:do...while会先执行语句再判断条件,也就是说最少执行一次语句。for 写在for循环判断条件中的声明变量会引入新的作用域,外面无法访问。如for(int i=0;i<10;i++){} 此处i外部无法访问

8、流程控制关键字

        break (循环或switch中使用)

        循环可以起标签名,break可以根据标签名结束对应循环

        continue(循环中使用)

        return 方法中用

9、方法

方法类型 返回值 方法名(参数){方法体}

注:方法不能嵌套定义,即方法里面不能定义方法

        同一个类中,可以出现名一样得方法,但参数不能相同(重载)

10、数组

1、动态初始化:

数据类型[] 变量名 = new 数据类型[m]

例如:int[] arr = new int[3] (new表示在堆内存创建地址)

new后会自动赋予初始值:

        整形:0

        浮点型:0.0

        字符型:/u0000

        布尔型:false

        引用类型:null

2、静态初始化:

数据类型[] 变量名 = new 数据类型[]{数据1,数据2 ......}

简化写法: 数据类型[] 变量名 = {数据1,数据2 ......}

注:不用给出数组大小,系统会自动判断

        length属性获取数组长度,但获取的只是为数组分配 的空间的数量,而不是数组中实际已经存放的元素的个数。

3、二维数组

格式一:数据类型[][] 数组名 = new 数据类型[m][n];

格式二:数据类型[][] 数组名 = new 数据类型[m][] (会自动判断长度,各行长度不同不补零)

格式三:数据类型[][] 数组名 = new 数据类型[][]{{.....},{}.....} (会自动判断长度,各行长度不同不补零)

简写:数据类型[][] 数组名 = {{.....},{}.....}

注:二维数组第一维存的是第二维的地址

11、面向对象

面试题:什么是面向对象?

面向对象(Object Oriented)是软件开发方法,一种编程范式。面向对象的概念和应用已超越了程序设计软件开发。面向对象是一种对现实世界理解和抽象的方法,是计算机编程技术发展到一定阶段后的产物。

面向对象是相对于面向过程来讲的,面向对象方法,把相关的数据和方法组织为一个整体来看待,分为成员属性和成员方法。从更高的层次来进行系统建模,更贴近事物的自然运行模式。

注:一个java文件中可以有多个类,但只有一个与文件名相同的类才能被public修饰

        匿名对象:没有名字的对象 例如:new Person(); (其实就是new出来没有用变量接受)

1、封装

1、概述

封装是把过程和数据包装起来,对数据的访问只能通过已定义的接口

        1、对数据(属性)的封装使用private

        2、对过程(行为)的封装使用,给出访问权限为public的setter(给数据赋值,不需要返回值)方法和getter(获得赋值后的数据,需要返回值)方法

2、this关键字

注:1、谁调用指向谁 2、就近原则 (this.方法/属性)

3、修饰符范围 private => 默认 => protect => public

        private只能在同一个类中使用。

        默认的无修饰符,在同一个类中和同一个包中子类无关类。

        protected在同一个类中和同一个包中子类无关类、不同包的子类。(外面的儿子也是受保护的)

        public在同一个类中和同一个包中子类无关类、不同包的子类、不同包的无关类。

4、构造方法

        方法名与类名相同,不能有返回值类型(可用于初始化操作)

5、static 关键字

        1、被类的所有对象共享。2、可直接通过 类名.静态属性/方法 调用(推荐)。

注:静态方法 只能调用 静态属性和方法(没有this关键字),非静态方法都可以调用

6、工具类 (记得加上注释)

        1、构造方法私有化

        2、成员方法必须是静态的

7、代码块

        局部代码块: { ....... } 可限制作用域和

        构造代码块: 放在构造函数位置,自动执行,初始化操作,执行顺序高于构造函数

        静态代码块:放在构造函数位置,static关键字修饰,只在程序最开始运行一次

        同步代码块

2、继承

1、继承。

         注: 虚方法表里的才能被重写。

        子类访问权限要大于或等于父类。

        super.绕过子类调用父类方法。

        重写的方法最好加上注解。

        父类不能被多次初始化,多次初始化只有第一次生效。

        子类没法重写父类的静态方法,但可以声明重名的静态方法,两者没有任何关系。

2、this和super的区别:

this访问本类中的属性,如果本类没有此属性则从父类中继续查找。super直接访问父类中的属性。

3、子类和父类构造方法:

        1.如果子类没有定义构造方法,则默认调用父类的无参数的构造方法。

        2.如果子类定义了构造方法,不论是有参还是无参的构造方法,在创建子类的对象的时候,首先执行父类无参数的构造方法,然后执行自己的构造方法。

        3.想要执行子类的时候调用父类的有参构造方法,可以在子类的构造方法的第一句加super(父类的参数)

        4.如果父类里面有有参构造方法,这样就没有了无参构造方法,但是执行子类的时候必须调用父类的无参,所以会报错

        5.要让4不出现错误,必须在子类的的构造方法的第一句调用父类的有参,那么就不会调用无参了,就不会出错了,所以在子类的构造函数里面加super(参数) 6.new出一个子类对象时,没有特别处理的时候,必须先调用父类的有参构造方法或者无参构造方法的一种,然后再调用子类的构造方法

注:构造方法不能嵌套使用,可以用 this(参数) 关键字在一个构造方法中调用别的构造方法。

        构造方法不能被继承,可以通过 super(参数) 关键字调用父类的构造方法,达到重写的目的。

4、重载和重写的区别:

override(重写):

   1、方法名、参数、返回值相同。

   2、子类方法不能缩小父类方法的访问权限。

   3、子类方法不能抛出比父类方法更多的异常(但子类方法可以不抛出异常)。

   4、存在于父类和子类之间。

   5、方法被定义为final不能被重写。

overload(重载):

  1、参数类型、个数、顺序至少有一个不相同。

  2、不能重载只有返回值不同的方法名。

  3、存在于父类和子类、同类中。

5、final 关键字

注:final 修饰基本数据类型,变量值不可被修改

        final 修饰引用数据类型,引用数据类型地址不可被改变

        final 修饰方法,不能被子类重写

        final 修饰类,类不能被继承

3、多态

1、多态的前提:

        要有继承关系。

         要有方法的重写,没有也行,但无法表现出多态的特点。

         要有父类的引用指向子类的对象

2、多态访问成员变量的特点:

        属性看左边,方法看右边,静态方法看左边 (即除了普通方法调用子类外,其他都调用父类的数据)

3、多态的好处:

        提高了程序的维护性(由继承保证)

         提高了程序的扩展性(由多态保证)

4、向下转型:

        向下转型是把父类对象转为子类对象。

Animal a = new Cat();
Cat c = (Cat) a;
c.eat();
//输出  我吃鱼  (注:标准向下转型,转换类型是实际类型的同等或父类关系)
Dog d = (Dog) a;
d.eat();
// 报错 : java.lang.ClassCastException  (转换类型与实际类型不同无关系)
Animal a1 = new Animal();
Cat c1 = (Cat) a1;
c1.eat();
// 报错 : java.lang.ClassCastException  (转换类型是实际类型的子类)

12、抽象类

        1、抽象类 abstract 关键字,可修饰类和方法

        2、抽象类中可以有非抽象方法,但有抽象方法必须是抽象类。

        3、抽象类无法被实例化

        4、抽象类中有构造方法,作用是在继承时,初始化子类

        5、子类必须重写父类的所有抽象方法

        6、抽象类也可以实现多态

        7、抽象类也可以继承抽象类

        8、抽象类中的属性(变量和常量)同正常类一样

        9、abstract 与 final,static,private 关键字冲突,不能在同一个方法共存

13、接口

        1、接口中只能定义抽象方法,默认在方法的前面添加public和abstract关键字

        2、接口和接口之间可以多继承,但类和类之间只能单继承

        3、接口中只能有常量和方法,不能有变量

14、内部类

1、成员内部类:

        将一个类定义在成员位置的类

class Person{
    class Student{  //此处是成员位置,因为成员属性和成员方法都写在这            
    }
}

2、静态内部类:

        内部类前加static关键字

实例化:外部类.内部类 对象名 = new 外部类.内部类() (有点类似调用类中静态方法)

注:静态内部类中的静态方法调用格式:外部类.内部类.静态方法() (类名一路点到底)

3、局部内部类:

        定义在方法中的类,因作用域只有方法局部,所以称为局部内部类

注:若方法中含有局部内部类,那么该方法中的所有变量默认加上final修饰符。

局部内部类的方法想要访问内部类的变量,通过 this.变量 调用,若要访问外部类的变量,则需要 外部类名.this.变量 来调用

4、匿名内部类:

        没有名字的内部类

格式和实例:

//new 类名或者接口名() {  //要么是实现关系,要么是继承关系,即要么是接口,要么是父类。
//  重写方法;             //返回值为相应类或接口
//};
new Inter() { 
    public void show(){
        //代码......    
    }
}; //此处要加分号。返回值是Inter接口类型

15、常用API

1、Object类(所有类都继承Object类,所以直接.调用)

        getClass() 获取类名

        hashCode()

        toString(), 默认打印地址值。重写toString()方法,实现打印自定义类具体信息

        equals(),默认比较地址值,重写后实现比较自定义类具体数据

        finalize() 垃圾回收

        clone() 浅拷贝。不能直接调用,需要实现Cloneable接口,并重写 clone() 方法才行

注:标记接口:接口中没有方法,做标记表示可以进行某些特殊操作

2、Scanner:键盘录入

        nextLine() 可以接受特殊字符

        hasNextXxx() 判断输入的是否是Xxx类型,返回值为bool类型(可避免输入数据不是指定数据)

3、String 类

1)String的构造函数:

        1、String() 无参构造

        2、String( Byte[] bytes) 会把数组中的值转换为字符,然后连接起来

        3、String( Byte[] bytes,int index, int length) 从索引为index开始,去指定长度,转换为字符连接起来

        4、String(char[] chars) 把字符连接为字符串

        5、String(char[] chars, int index, int length)

2)获取功能:(查)

        charAt(索引) 返回指定索引处的值

        indexOf(值) 返回指定值的索引,值可为int型(阿斯克码值),可为一个字符或一段字符串

        indexOf(值,索引) 从指定索引开始找值,返回索引

        substring(开始索引,结束索引) 字符串截取,结束索引不写默认截取到最后,左闭右开

        endsWith(String end) 判断是否为end字符串结尾

3)转换功能:

        getBytes() 将字符串转换为字节数组

        toCharArray() 转换为字符数组

        valueOf(char[] chars) 将字符数组转换为字符串(静态方法)

        toLowerCase() 所有字符转为小写

        toUpperCase() 所有字符转为大写

        concat(String str) 字符串拼接(str1.concat(str2))将str1和str2拼接起来

4)其他功能

        replace(old, new) 参数可以是一个字符或一段字符串(只替换第一个)

        replaceAll(old, new) (全部替换)

        trim() 去除前后空格

        compareTo(str) 按阿斯克码值比较两个字符串,不用在意返回值大小,只要在意正负。

实例:str1.compareTo(str2) 返回值为负,则str1

4、StringBuffer(线程安全)

1、构造方法:

        1、StringBuffer() 无参构造,默认大小16(每次扩容*2+2)

        2、StringBuffer(int capacity) 指定初始大小

        3、StringBuffer(String str) 赋初始值,默认大小为(给定字符串的长度+16)

2、常用方法:

        capacity() 总容量

        length() 获取存储的数据长度(实际存储的长度)

增:

        append(数据) 追加

        insert(索引,数据) 在指定位置添加数据

删:

        deleteCharAt(索引) 删除指定索引的一个字符

        delete(开始索引,结束索引) 删除一段数据(左闭右开)

改:

        replace(开始替换索引,结束替换索引,新字符串) 将指定索引的一段字符串替换为新的字符串

反转:

        reverse() 字符串反转

截取:

        substring(开始索引,结束索引) 返回截取后的字符串,不写结束索引默认到最后

转回String类型:

        toString()

        new String(StringBuffer str) StringBuffer 作参数,返回String类型

5、Character包装类

构造方法:

        Character(char c) 包装成包装类

常用方法:

        isUpperCase(char c) 判断字符是否为大写

        isLowerCase(char c) 判断字符是否为小写

        isDigit(char c) 判断是否为数字字符

6、Random类

构造方法:

        Random() 无参构造

常用方法:

        nextInt() 随机生成一个数

        nextInt(数) 传入一个数,生成一个0-指定值的数,左闭右开

7、System类 (与系统相关的类)

常用方法:(都是静态方法)

        gc() 垃圾回收

        exit(参数) 关闭虚拟机,参数为0,正常退出,非0为非正常退出

        currenctTimeMilles() 返回当前时间戳

8、Date类

构造方法:

        Date() 无参构造,返回当前格式化完整时间

        Date(时间戳) 赋初值构造,返回值为传入的时间戳格式化完整时间

9、simpleDateFormat类

构造方法:

        simpleDateFormat(自定义格式) 返回当前时间,格式推荐除月份大写(助记MM,大美眉),其余都小写

16、Collection集合

1、Collection

常用方法:

        add(数据)

        remove(数据)

        clear() 清空

        contains(数据) 判断是否包含指定数据,自定义类需要重写equals方法

        isEmpty()

        size()

        addAll(集合) 添加另一个集合中的元素

        removeAll(集合) 删除另一个集合中的元素,可部分删除

        containsAll(集合) 要全部包含

        retainAll(集合) 取交集,改变调用者数据

        toArray() 转化为数组

遍历:

        1、转换为数组后,遍历

        2、迭代器

2、List

List 集合新增方法:

索引相关:

        add(索引,数据) 在指定索引添加指定数据

        remove(索引) 删除所有位置元素

        get(索引) 获取指定索引的元素

        set(索引,数据) 修改指定索引的元素为指定数据

        listIterator() 列表迭代器(可后移遍历,也可前移遍历,但只有一个游标,且默认在前面,所以需要先正着遍历,才能倒着遍历。有add方法,可以在遍历中添加数据)

数据结构:

1、平衡二叉树:

        任意一个分支节点的左右子树高度差不超过1

2、红黑树:

        性质1.节点是红色或黑色。

        性质2.根是黑色。

        性质3.所有叶子都是黑色(叶子是NIL节点)。

        性质4.每个红色节点必须有两个黑色的子节点。(从每个叶子到根的所有路径上不能有两个连续的红色节点。

        性质5.从任一节点到其每个叶子的所有简单路径都包含相同数目的黑色节点。

3、hash碰撞解决方案:

法1:链地址法

法2:再哈希法 。提供多个哈希函数,如果第一个哈希函数计算出来的key的哈希值冲突了,则使用第二个哈希函数计算key的哈希值。

法4:开放定址法:(三个具体方法)

        线性探测再散列

        二次(平方)探测再散列

        伪随机探测再散列

4、哈夫曼树:

给定N个权值作为N个叶子结点,构造一棵二叉树,若该树的带权路径长度达到最小,称这样的二叉树为最优二叉树,也称为哈夫曼树。哈夫曼树是带权路径长度最短的树,权值较大的结点离根较近。

1、ArrayList

        底层是数组,查询快,增删慢,线程不安全,效率高。

三种遍历方法:

        1、转数组后遍历

        2、迭代器

        3、普通for循环,根据索引取值遍历

2、Vector

        底层也是数组,查询快,增删慢,线程安全,效率低。(不常用)

3、LinkedList

        底层是双链表,查询慢,增删快,线程不安全,效率高。

新增方法:

        addFirst(数据)

        addLast(数据)

        getFirst()

        getLast()

        removefrist()

        removeLast()

4、泛型:

        在定义的时候,限制对象存储的数据类型。

        泛型类,泛型接口,泛型方法

增强for循环:

格式:

for(数据类型 变量名:要遍历的数据){
    //变量会依次接收每一个元素,进行操作
}

静态导入:

使用的方法是静态的,导入静态包后,直接使用,不用再 类名.静态方法 调用

例如:

import static java.util.Math.* //静态导入
class Test {
    public static void main(){
        int a = 2, b = 3;
        System.out.println(max(a, b)) //此处可以求出最大值,如果不静态导入需要写成:Math.max(a, b);    
    }
}

可变参数:

(可变参数只能放在参数列表最后,且唯一)

public static int sum (int... x){ //示例求n个之和的方法,传入的参数都存到x中,x是一个数组
    int sum = 0;
    for(int i = 0; i < x.length; i++){
            sum += x;
    }
    return sum;
}

3、Set

特点:无序,元素唯一不可重复

1、hashSet:

        底层是哈希表

        自定义类去重需要在类中重写 equals() 和 hashcode() 方法,否则无法检测是否相同

2、LinkedHashSet

        是 hashSet 的子类,底层是哈希表和链表,有序但无索引

3、TreeSet

        底层是红黑树,默认升序,可自定义排序规则(比较器排序)

自定义排序规则的两种方式:

        1、自然排序:自定义JavaBean类实现 Compareble 接口,重写 CompareTo() 方法。this 是待排序的数,o 是已经排序好的值。判断条件要包含所有属性字段,不然会根据排序的几个字段误去重所有字段的值。

        2、比较器排序:定义TreeSet时,传入自定义比较器参数,重写Compare()方法。建议使用lambda表达式(推荐)或匿名函数

17、Map键值对

1、成员方法

        put(key, value) 注:后面添加相同的键,值会被覆盖

        remove(key)

        clear()

        containsKey(key) 判断是否包含指定键

        containsValue(value) 判断是否包含指定值

        isEmpty()

        size()

        get(key) 根据key获取value值

        keySet() 获取所有的key,返回值为Set集合

        values() 获取所有的value,返回值为Colletion集合

        entrySet() 获取所有的键值对,返回值是Set集合,每一个键值对是一个单独的小Map.Entry集合

注:Entry集合中方法:getKey() 获取键,getValue获取值

遍历的两种方式:

        1、先获取所有的键,然后遍历所有的键,通过get(key)获取value值

        2、获取所有的键值对,然后遍历所有的键值对,

1、HashMap

2、LinkedHashMap

3、TreeMap

HashMap和Hashtable的异同

        同:都是键值对

        异:HashMap线程不安全,Hashtable线程安全。HashMap的键和值允许为null,Hashtable键和值不允许为null

4、Collections工具类

成员方法:(都是静态)

        sort(List list) 排序(只对List集合排序,弥补没有TreeList的缺点。底层是归并排序)

        binarySearch(List list, 数据) 在指定有序数组中二分查找指定数据

        max(Collection<> col) 返回最大值

        reverse(List list) 反转

        shuffle(List list) 打乱数据

        synchronizedList(List)

        synchronizedSet(Set)

        synchronizedMap(Map) 把线程不安全的集合变成安全的集合

18、异常:java程序在编译或运行中出现的错误

异常的实现方式:

        1、try ... catch ... finally ....

        2、throws

方法:printStackTrace

19、File

构造方法:

        new File(String pathName)

        new File(String parent, String child)

        new File(File parent, String child)

成员方法:

        createNewFile() 创建文件,无法创建文件夹,父文件夹需要存在

        mkdir() 创建单级文件夹

        mkdirs() 创建多级文件夹

        delete() 删除一个文件或文件夹,不能删除非空文件夹

        renameTo(File newName) 重命名

判断方法:

        isDirectory() 判断是否是文件夹

        isFile() 判断是否是文件

        exists() 判断是否存在

        canRead() 是否可读

        canWrite() 是否可写

        isHidden() 是否隐藏

        getAbsolutePath() 获取绝对路径

        getPath() 获取相对路径

        getName() 获取文件名

        length() 获取文件字节数

        lastModified() 获取最后一次修改时间,返回值是毫秒

        list() 获取当前目录下所有的文件和文件夹名字,返回值为字符串数组

        listFiles() 获取当前目录下所有的文件和文件夹 File 对象,返回值为 File 类型数组

        list( FilenameFilter )

        listFiles( FilenameFilter )

FilenameFilter 是一个函数式接口过滤器,重写方法后,会根据返回值判断是否留下或过滤。方法中两个参数为目录和文件名。

20、IO流

字节流:InputStream,OutputStream (不可读取汉字等,常用于非文本文件操作)

字符流:Reader,Writer (可读取汉字等,常用于文本文件操作)

1、字节流

1、FileOutputStream 写出

(注:一旦创建对象,不追加,会清空文件内所以内容)

构造方法:FileOutputStream( 字符串形式文件路径或File对象, bool append ) 第二个参数代表是否开启追加

注:只可创建文件不能创建文件夹,即创建文件父文件夹一定要存在

方法:

        write(int i) 写入一个字节字符(会根据阿斯克码值转换为字符)

        write(byte[] byte, int off, int len) 写入一个字符数组,参数二是开始写入索引,参数三是要写入字符的长度,可以没有后面两个参数,默认全部写入。(此处想写入字符串,可以先把字符串声明,然后利用getBytes()方法,把字符串转换为字符数组)

public static void main(String[] args) throws IOException {
    FileOutputStream fos = new FileOutputStream("C:\\customFiles\\javaFiles\\test1\\a.txt");
    String hello = "helloworld";
    byte[] bytes1 = hello.getBytes();
    fos.write(bytes1);
    fos.close(); //不要忘记关闭资源
}

2、FileInputStream 读取数据

常用方法:

        read() 一次读取一个字符,默认转换为阿斯克码值对应的数字,读到末尾返回-1

public static void main(String[] args) throws IOException {
    FileInputStream fis = new FileInputStream("C:\\customFiles\\javaFiles\\test1\\a.txt");
    int words = fis.read(); //文件中数据:helloworld
    System.out.println(words); //输出104(h的阿斯克码值)
    fis.close();
}

循环读取:(最标准写法)

public static void main(String[] args) throws IOException {
    FileInputStream fis = new FileInputStream("C:\\customFiles\\javaFiles\\test1\\a.txt");
    int word; //文件中数据helloworld
    while ((word = fis.read()) != -1) {
        System.out.print((char) word); //依次输出helloworld
    }
    fis.close();
}

拷贝文件操作 :

public static void main(String[] args) throws IOException {
    FileInputStream fis = new FileInputStream("C:\\customFiles\\javaFiles\\test1\\a.txt");
    FileOutputStream fos = new FileOutputStream("C:\\customFiles\\javaFiles\\test1\\b.txt");
    int word;
    while ((word = fis.read()) != -1){
        fos.write(word);
    }
    fos.close();
    fis.close(); //先开的资源最后再关
}

一次读取多个字符

        read( byte[] byte ) 一次读取传入的数组长度个字符,推荐1024的倍数。返回值为读取到的数据长度

public static void main(String[] args) throws IOException {
    FileInputStream fis = new FileInputStream("C:\\customFiles\\javaFiles\\test1\\a.txt");
    byte[] bytes = new byte[2]; //以两个为例
    int reads;
    while ((reads = fis.read(bytes)) != -1) {
        System.out.println(reads); //依次输出 2 2 1
        System.out.println(Arrays.toString(bytes)); //依次输出[104, 101][108, 108][111, 108](最后一个108是因为数据的脏数据) 
    }
}
//要想解决上述情况,可以在读取数组时限制读取的长度即可。如:
public static void main(String[] args) throws IOException {
    FileInputStream fis = new FileInputStream("C:\\customFiles\\javaFiles\\test1\\a.txt");
    byte[] bytes = new byte[2]; //以两个为例
    int reads;
    while ((reads = fis.read(bytes)) != -1) {
        System.out.println(reads); //依次输出 2 2 1
        for (int i = 0; i < reads; i++) { //以读取到的数据长度为数组输出长度,防止脏数据
            System.out.print(bytes[i]+" ");//依次输出"104 101" "108 108" "111"        }
        System.out.println();
    }
}

2、字符流 (带有缓冲区)

字符流跟字节流方法一致

读取含汉字文本文件案例:

public static void main(String[] args) throws IOException {
    FileReader fr = new FileReader("C:\\customFiles\\javaFiles\\test1\\a.txt");
    int read; // 文件中数据:hello你好啊!
    while ((read = fr.read()) != -1)
        System.out.print((char) read); //依次输出hello你好啊!
}

写入示例:

public static void main(String[] args) throws IOException {
    FileWriter fw = new FileWriter("C:\\customFiles\\javaFiles\\test1\\c.txt");
    fw.write("你好世界!"); //在指定文件中写入了:你好世界!
    fw.close();
}

3、缓冲流 (默认8192个字节的缓冲区)

缓冲区在内存,读写都在内存,读取完或到达缓冲区最大容量一次写入硬盘,所以速度快,效率高。

1、字节流构造方法:

(将基本流包装成高级流,提升效率。第二个参数是指定缓冲区大小,不写就默认8192个字节的缓冲区)

        BufferedInputSteam(InputStream is) 读取

        BufferedOutputStream(OutputStream os) 写入

基本方法和使用同基本流

2、字符流构造方法:

        BufferReader(Reader r)

        BufferWriter(Writer w)

新增方法:

        readLine() 读取一行数据,读取完返回null(注:不会读取一行结束的换行。读取数字等常用,返回值为String类型,直接再用Integer.parseInt() 方法就可以转回数字)

        newLine() 跨平台的换行(直接调用默认自动写入文件)

注:推荐以后文本文件直接用字符缓冲流,特殊操作用字节缓冲流

4、转换流(可转换编码格式,防止乱码)

构造方法:

        InputStreamReader(FileInputStream fs, 编码格式) 根据给定编码格式读取指定文件

        OutputStreamWriter(FileOutputStream fo, 编码格式) 根据指定编码格式写出文件

常用方法同字符流

以上方法JDK11后被淘汰了,新方法就是字符流加上编码格式参数:

构造方法:

        FileReader(File对象或字符串路径,Charset.forName(编码格式)) 读取

        FileWriter(File对象或字符串路径, Charset.forName(编码格式)) 写出

练习:将本地的GBK文件,转换为UTF-8文件。(一定别忘了关闭资源!!!)

public static void main(String[] args) throws IOException {
    InputStreamReader ip = new InputStreamReader(new FileInputStream("C:\\临时\\a.txt"), "GBK");
    int i; //文件中内容为:你好啊!!
    ArrayList<Integer> list = new ArrayList<>();
    while ((i = ip.read()) != -1) {
        list.add(i);
    }
    OutputStreamWriter ow = new OutputStreamWriter(new FileOutputStream("C:\\临时\\a.txt"), StandardCharsets.UTF_8);
    for (Integer integer : list) {
        ow.write(integer);
    } //修改后文件内容不变,编码格式变为UTF-8
    ow.close();
    ip.close(); //一定别忘了关闭资源!!!!!
}

5、序列化流:可以把对象写入文件中

序列化流:

构造方法:

        ObjectOutputStream(OutputStream out) 写出操作。把字节流包装成序列化流

成员方法:

        writeObject(Object obj) 把对象序列化写出到文件中去

注:直接写进文件会报 NotSerializableException 异常,需要JavaBean类继承 Serializable 接口才行。

Serializable:标记型接口,代表可以序列化,不需要别的操作。示例如下:

public static void main(String[] args) throws IOException {
    Student alice = new Student("alice", 18);
    ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream("C:\\临时\\a.txt"));
    os.writeObject(alice);
    os.close();
}

//Student类需要实现Serializable接口
public class Student implements Serializable {  ....  }

反序列化流:

构造方法:

        ObjectInputStream(InputStream input) 读取操作,把字节流变成高级流

成员方法:

        readObject() 把序列化的文件读取到程序中

public static void main(String[] args) throws IOException {
    ObjectInputStream ois = new ObjectInputStream(new FileInputStream("C:\\临时\\a.txt"));
    Student student = (Student)ois.readObject(); //此处要强转
    System.out.println(student);
    os.close();
}

修改JavaBean中的内容会导致反序列化失败,原因是修改后JavaBean的版本号发生了变化,解决方法有:

1、在JavaBean中固定版本号即可。只需要在JavaBean中加上属性:

private static final long serialVersionUID = 1L;
//serialVersionUID代表版本号常量,后面的值可随意给,不改变就行

2、修改Idea设置

打开设置,搜索Serializable,把下面两项勾上即可。后续JavaBean类名会报黄,重写提示即可

transient 关键字:

在JavaBean成员变量前加上 transient 关键字,则该属性不会被序列化到文件中,反序列化输出相应数据类型初始值。示例:

private transient String address; //String类型的值初始值为null,所有反序列化时,该值显示为null

练习:写入并读取多个对象。

    public static void main(String[] args) throws IOException, ClassNotFoundException {
//        写入
        Student alice = new Student("alice", 18);
        Student tom = new Student("Tom", 20);
        ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream("C:\\临时\\a.txt"));
        os.writeObject(alice);
        os.writeObject(tom);
        os.close();

        //读取
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("C:\\临时\\a.txt"));
        Student student = (Student)ois.readObject(); //此处要强转
        System.out.println(student);
        Student student2 = (Student)ois.readObject(); //读取完第一个对象后,光标会移动到第二个对象处
        System.out.println(student2); //读取完读取不到数据会直接报错,所以在不知道存几个对象的情况下读取非常麻烦
        ois.close();
    } //控制台输出:第一行:Student{name='alice', age=18} 第二行:Student{name='Tom', age=20}

上述在不知道存几个对象的情况下读取非常麻烦,推荐存的时候就存到一个ArrayList集合中,例如:

public static void main(String[] args) throws IOException, ClassNotFoundException {
    //写入
    Student alice = new Student("alice", 18);
    Student tom = new Student("Tom", 20);
    ArrayList<Student> list = new ArrayList<>();
    ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream("C:\\临时\\a.txt"));
    list.add(alice);
    list.add(tom);
    os.writeObject(list); //把数据存到集合中,直接把集合对象存进去
    os.close();
    //读取
    ObjectInputStream ois = new ObjectInputStream(new FileInputStream("C:\\临时\\a.txt"));
    ArrayList<Student> students = (ArrayList<Student>) ois.readObject(); //此处强转为Student类型的List集合
    System.out.println(students); //控制台输出:[Student{name='alice', age=18}, Student{name='Tom', age=20}]
    ois.close();
}

6、打印流

打印流一般指:PrintStream, PrintWriter 两个类

特点:

        打印流只操作文件目的地,不操作数据源(即只写出数据,不能读取数据)

        特有的写出方法,数据原样写出

        可以实现自动刷新,自动换行(自动刷新的意思是会把缓冲区的数据写入文件,实时写入,不是关流是一次写入)

1、字节打印流:

构造方法:

        PrintStream(OutputStream/File/String) 关联字节输出流/文件/文件路径

        PrintStream(文件路径,编码格式) 指定编码格式

        PrintStream(OutputStream, boolean autoFlush) 是否自动刷新(字节流底层没有缓冲区,所以开不开自动刷新都一样,此构造方法不用)

成员方法:

        writer(int) 和字节流用法一样

新增特有方法:

        println(数据) 打印任意类型数据,自动刷新,自动换行

        print(数据) 打印任意数据,不换行

        printf( ... ) 带有占位符的打印语句,不换行,同c语言 printf() 语句

public static void main(String[] args) throws FileNotFoundException {
    PrintStream ps = new PrintStream("C:\\临时\\PrintStrem.txt");//默认utf-8编码格式
    ps.println(111);//数据原样写入,自动换行加刷新(需要在构造方法中打开刷新)
    ps.print(true);//新增特有方法全部数据原样写入
    ps.printf("我%d岁了", 18);//同c语言带占位符写入
    ps.close();
} //在相应文件中写入数据:第一行111 第二行:true我18岁了

2、字符打印流

构造方法:

        PrintWriter(Writer/File/String) 关联文件

        PrintWriter(文件路径,编码格式) 指定编码格式(不指定默认utf-8)

        PrintWriter(Writer, boolean autoFlush) 自动刷新(字符流有缓冲区,可以开启)

        PrintWriter(OutputStream, autoFlush, 编码格式)

常用方法同字节打印流

7、解压缩流和压缩流

8、Commons-io 工具包

需要导包。有两个类,都是静态方法

1、FileUtils类:

        copyFile(File old, File new) 复制文件

        copyDirectory(File oldDir, File newDir) 复制文件夹中的内容

        copyDirectoryToDirecory(File oldDir, File newDir) 复制文件夹(连外层文件夹也复制进去)

        deleteDirectory(File dir) 删除文件夹

        cleanDirectory(File dir) 清空文件夹

        readFileToString(File file, 编码格式) 读取文件中的数据变成字符串作返回值

        writer(File file, )

2、IOUtils类

        copy(InputStream input, OutputStream output) 复制文件

        copyLarge(Reader input, Writer output) 复制大文件

        readLines(Reader input) 读取一行数据并作返回值

        writer(String data, OutputStream output) 写出数据

9、Hutool 工具包

10、Properties配置文件

Java关联配置文件:

        Properties prop = new Properties();

        prop.load( Reader );

常用方法:

        getProperties(key) 获取指定键的值

        .........查API文档(很少用)

21、多线程

进程:正在运行的程序,由系统分配资源和调用的独立单元。每一个进程都有自己的内存空间和系统资源

线程:线程是操作系统中调度执行的基本单位。一个线程是一个“执行流”,每个线程之间都可以按照顺序执行自己的代码,多个线程“同时”执行多份代码。

1、java实现多线程的方法:三种。

第一种实现方式:继承 Thread 类

public class Test1 {
    public static void main(String[] args) {
//        1、自己定义一个类继承Thread
//        2、重写run方法
//        3、创建子类的对象,并启动线程
        Mythread1 myThread1 = new Mythread1();
        MyThread2 myThread2 = new MyThread2();
        myThread1.setName("线程1:"); //起个名字,区分两个线程
        myThread2.setName("线程2:");
        myThread1.start(); //两个线程是同时进行,
        myThread2.start(); //不是按照代码前后顺序,先执行线程1,再执行线程2
    }
}

class Mythread1 extends Thread { //测试线程1,打印hello 100次
    @Override
    public void run() {
        for (int i = 0; i < 100; i++)
            System.out.println(getName() + "hello");
    }
}

class MyThread2 extends Thread { //测试线程1,打印world 100次
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println(getName() + "world!");
        }
    }
}

第二种实现方法:实现 Runnable 接口

public class Test2 {
    public static void main(String[] args) {
//        1、自己定义一个类实现Runnable接口
//        2、重写里面的run方法
//        3、创建自己的类的对象
//        4、创建一个Thread类的对象,并开启线程
        MyRun myRun = new MyRun();
        Thread thread1 = new Thread(myRun);
        Thread thread2 = new Thread(myRun);
        thread1.setName("线程一");
        thread1.start();
        thread2.setName("线程二");
        thread2.start();
    }
}
class MyRun implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
//此处因为该类没有继承线程方法,所以无法使用getName()方法。
//所以必须利用线程的静态方法currentThread()方法,获取线程,然后获取线程名字
            Thread thread = Thread.currentThread();
            System.out.println(thread.getName() + "hello world!");
        }
    }
}

第三中实现方式:实现 Callable 接口

public class Test3 {
    //特点:可以获取到多线程运行得结果
    //1、创建一个类实现Callable接口
    //2、重写call(有返回值,为包装类类型,表示多线程运行得结果)
    //3、创建自定义类得对象(表示多线程要执行得任务)
    //4、创建FutureTask的对象(作用管理多线程运行的结果)
    //5、创建Thread类的对象,并启动(表示线程)
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        MyCallable myCallable = new MyCallable();
        //一个FutureTask任务表示一个将要执行的进程任务
        FutureTask<Integer> task1 = new FutureTask<>(myCallable);
        FutureTask<Integer> task2 = new FutureTask<>(myCallable);
        Thread thread1 = new Thread(task1); //Thread创建具体进程
        thread1.setName("线程1: ");
        Thread thread2 = new Thread(task2);
        thread2.setName("线程2: ");
        thread1.start();
        thread2.start();
        System.out.println(task1.get()); //控制台输出100
        System.out.println(task2.get()); //控制台输出100
    }
}
class MyCallable implements Callable<Integer> {
    @Override
    public Integer call() throws Exception {
        int i;
        for (i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName() + "hello world!");
        }
        return i;
    }
}

第一种方式:编程简单,可以直接使用Thread类中的方法。但可扩展性差,不能再继承其他类。

第二种方式:扩展性强,还可以继承其他类。但编程复杂,不能直接使用Thread类中的方法

第三种方式:扩展性强,还可以继承其他类,且可以设置获得返回值。但编程复杂,不能直接使用Thread类中的方法

2、成员方法:

        getName() 获取线程名

        setName(name) 设置线程名

        currentThread() 获取当前线程对象,静态方法,返回值为一个线程对象Thread

        sleep(long time) 睡眠,单位毫秒,静态方法

        setPriority(int) 设置线程优先级(1-10级,默认5。优先级只是概率,不是绝对,低优先级也有可能先执行)

        getPriority() 获取线程优先级

        setDaemon(boolean) 设置为守护线程(其他线程结束后,不管守护线程是否结束,都会中断进程)

        yield() 出让线程,静态方法(在自定义类中使用,让线程尽可能均匀轮流执行)

        join() 插入线程

3、同步代码块:

防止多个进程运行同一个代码,出现各种异常,可以给这段代码块加上锁 synchronized 。例如:

synchronized (一个对象) { //此行小括号内需要传入一个任意对象,无实际意义,但必须保证传且要唯一,建议用当前类的字节码文件:MyThread.class
    //...代码块
}

锁中的代码要等当前线程执行完,才会打开锁,下一个线程才能执行。

同步方法:synchronized 还可以放在方法修饰符后,给整个方法上锁

4、Lock锁

Lock锁要唯一,所以创建Lock锁对象时一定要加上 static 关键字

static Lock lock = new ReentrantLock();
//因为Lock是一个接口,不能实例化,所以必须实例化其实现类

示例:

static Lock lock = new ReentrantLock();
lock.lock(); //加锁
    //....代码块
lock.unlock(); //解锁

5、线程的状态:

6、线程池:(Executors工厂类)

常用方法:

        newCacheThreadPool() 基于缓存

        newFixedThreadPool(int counts) 创建一个指定固定大小的缓冲池

        newScheduledThreadPool()

        newSingleThreadExecutor()

7、匿名内部类实现多线程:(面试常问,开发不长用)

8、定时器:Timer

常用方法:

        schedule(TimerTask task, long delay) 间隔指定时间执行一次定时任务

        schedule(TimerTask task, long delay, long period) 间隔指定时间delay执行一次定时任务,过period时间后再次执行任务,并不断重复。

定时任务:TimerTask

        run() 定时任务写在这个方法中

        cancel() 取消定时

import java.util.Timer;
import java.util.TimerTask;
 
public class TimerExample {
    public static void main(String[] args) {
        Timer timer = new Timer();
        TimerTask task = new TimerTask() {
            @Override
            public void run() {
                System.out.println("hello world!!!");
            }
        };
        long timeDelay= 1000;
        timer.schedule(task, timeDelay, timeDelay);
    }
} //每个一秒打印一次hello world!!!

22、网络编程

1、三要素:IP,端口,协议

2、InetAddress类:

这个类没有构造方法,但我们可以用静态的成员方法获取该类的对象

常用方法:

        getByName(String host) 返回传入 host 的 InetAddress 对象(静态方法)

        getHostName() 得到主机名

        ........API文档

3、TCP/UDP协议

UDP:将数据封装成数据包,不需要建立连接,数据快,不可靠协议,大小64k以内。

TCP:需要建立连接,形成传输通道,通过三次握手,是可靠连接。效率低,不限制数据大小。

4、Socket 套接字

1、DatagramSocket 类:

构造方法:

        DatagramSocket(int port) 构造数据报套接字并将其绑定到本地主机上的指定端口。

        DatagramSocket(int port, InetAddress laddr) 创建一个数据报套接字,绑定到指定的本地地址。

常用方法:

        send(DatagramPacket p) 从此套接字发送数据报包。

        receive(DatagramPacket p) 从此套接字接收数据报包。

        close() 关闭此数据报套接字。

2、DatagramPacket 类:

构造方法:

        DatagramPacket(byte[] buf, int length) 构造一个 DatagramPacket用于接收长度的数据包 length 。

        DatagramPacket(byte[] buf, int length, InetAddress address, int port) 构造用于发送长度的分组的数据报包 length指定主机上到指定的端口号。

常用方法:

        getData() 返回数据缓冲区。

        getLength() 返回要发送的数据的长度或接收到的数据的长度。

        getPort() 返回发送数据报的远程主机上的端口号,或从中接收数据报的端口号。

        setAddress(InetAddress iaddr) 设置该数据报发送到的机器的IP地址。

        setData(byte[] buf) 设置此数据包的数据缓冲区。

        setData(byte[] buf, int offset, int length) 设置此数据包的数据缓冲区。

        setLength(int length) 设置此数据包的长度。

        setPort(int iport) 设置发送此数据报的远程主机上的端口号。

3、UDP协议发送端代码:

        1:建立udp的socket服务

        2:将要发送的数据封装成数据包

        3:通过udp的socket服务,将数据包发送出

        4:关闭资源

public class UDPSend {
    public static void main(String[] args) throws IOException {
//        1:建立udp的socket服务
        DatagramSocket ds = new DatagramSocket();
//        2:将要发送的数据封装成数据包
        byte[] bytes = "helloworld".getBytes();
        DatagramPacket dp = new DatagramPacket(bytes, 0, bytes.length, InetAddress.getByName("192.168.23.8"), 12345);
//        3:通过udp的socket服务,将数据包发送出
        ds.send(dp);
//        4:关闭资源
        ds.close();
    }
}
UDP协议接收端代码:

        1:建立udp的socket服务.

        2:通过receive方法接收数据

        3:将收到的数据存储到数据包对象中

        4:通过数据包对象的功能来完成对接收到数据进行解析.

        5:可以对资源进行关闭

public class UDPReceive {
    public static void main(String[] args) throws IOException {
//        1:建立udp的socket服务.
        DatagramSocket ds = new DatagramSocket(12345);
//        2:通过receive方法接收数据   
//        3:将收到的数据存储到数据包对象中  
        byte[] bytes = new byte[1024];
        DatagramPacket dp = new DatagramPacket(bytes,bytes.length);
        ds.receive(dp);
//        4:通过数据包对象的功能来完成对接收到数据进行解析.
        byte[] data = dp.getData();
        String s = new String(data, 0, dp.getLength());
        System.out.println(s);
        System.out.println(dp.getAddress().getHostAddress());
//        5:可以对资源进行关闭
        ds.close();
    }
}

4、TCP协议客户端代码:

        1:建立客户端的Socket服务,并明确要连接的服务器。

        2:如果连接建立成功,就表明,已经建立了数据传输的通道.就可以在该通道通过IO进行数据的读取和写入.该通道称为Socket流,Socket流中既有读取流,也有写入流.

        3:通过Socket对象的方法,可以获取这两个流

        4:通过流的对象可以对数据进行传输

        5:如果传输数据完毕,关闭资源

public class TCPClient {
    public static void main(String[] args) throws IOException {
        //1:建立客户端的Socket服务,并明确要连接的服务器。
        Socket socket = new Socket("192.168.23.8", 12345);
        //2:如果连接建立成功,就表明,已经建立了数据传输的通道.就可以在该通道通过IO进行数据的读取和写入.该通道称为Socket流,Socket流中既有读取流,也有写入流.
        //3:通过Socket对象的方法,可以获取这两个流
        OutputStream outputStream = socket.getOutputStream();
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(outputStream));
        //4:通过流的对象可以对数据进行传输
        bw.write("hellowprld");
        bw.flush();
        socket.shutdownOutput();

        //接收反馈
        InputStream inputStream = socket.getInputStream();
        byte[] bytes = new byte[1024];
        int length = inputStream.read(bytes);
        String s = new String(bytes, 0, length);
        System.out.println(s);

        //5:如果传输数据完毕,关闭资源
        socket.close();
    }
}
TCP协议服务器端代码:

        1:建立服务器端的socket服务,需要一个端口

        2:服务端没有直接流的操作,而是通过accept方法获取客户端对象,在通过获取到的客户端对象的流和客户端进行通信

        3:通过客户端的获取流对象的方法,读取数据或者写入数据

        4:如果服务完成,需要关闭客户端,然后关闭服务器,但是,一般会关闭客户端,不会关闭服务器,因为服务端是一直提供服务的

public class TCPServer {
    public static void main(String[] args) throws IOException {
        //1:建立服务器端的socket服务,需要一个端口
        ServerSocket serverSocket = new ServerSocket(12345);
        //2:服务端没有直接流的操作,而是通过accept方法获取客户端对象,在通过获取到的客户端对象的流和客户端进行通信
        Socket accept = serverSocket.accept();
        //3:通过客户端的获取流对象的方法,读取数据或者写入数据
        InputStream inputStream = accept.getInputStream();
        BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));
        String s = br.readLine();
        System.out.println(s);
        System.out.println(accept.getInetAddress().getHostName());
        
        //发送接收成功反馈
        OutputStream outputStream = accept.getOutputStream();
        outputStream.write("接收成功!".getBytes());
        
        //4:如果服务完成,需要关闭客户端,然后关闭服务器,但是,一般会关闭客户端,不会关闭服务器,因为服务端是一直提供服务的
        serverSocket.close();
    }
}

练习:上传文件

客户端:

public class UploadFileClient {
    public static void main(String[] args) throws IOException {
        Socket socket = new Socket("192.168.23.8", 12345);
        InputStream inputStream = socket.getInputStream();
        BufferedReader br = new BufferedReader(new FileReader("C:\\临时\\诗句.txt"));
        OutputStream outputStream = socket.getOutputStream();
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(outputStream));
        String s = null;
        while ((s = br.readLine()) != null) {
            bw.write(s);
            bw.newLine();
        }
        bw.flush();
        socket.shutdownOutput();

        //接收反馈
        byte[] bytes = new byte[1024];
        int length = inputStream.read(bytes);
        String s1 = new String(bytes, 0, length);
        System.out.println(s1);

        socket.close();
    }
}

服务器端:

public class UploadFileServer {
    public static void main(String[] args) throws IOException {
        ServerSocket serverSocket = new ServerSocket(12345);
        Socket accept = serverSocket.accept();
        InputStream inputStream = accept.getInputStream();
        BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
        BufferedWriter bw = new BufferedWriter(new FileWriter("C:\\临时\\诗句2.txt"));
        String s = null;
        while ((s = reader.readLine()) != null) {
            bw.write(s);
            bw.newLine();
        }
        bw.flush();
        //反馈
        OutputStream outputStream = accept.getOutputStream();
        outputStream.write("接收成功".getBytes());

        serverSocket.close();
    }
}

23、类加载器和反射:

类加载器:把.class文件加载到内存,生成对象的Class对象

类加载器的分类:根类加载器,扩展类加载器,系统类加载器

反射:

反射允许对成员变量,成员方法和构造方法的信息进行编程访问

1、获取class对象的三种方式:

        1、Class.forName("全类名");(源代码阶段常用)

        2、类名.class;(加载阶段)

        3、对象.getClass();(运行阶段)

注:全类名是包名+类名。例如:com.itcast.www.Student。

第一种最常用,第二种一般当参数使用,第三种已new对象后用。

要获取类后才能获取构造方法,成员变量,成员方法等。

2、获取构造方法:

        getModifiers() 获取权限修饰符,返回值为int,分别代表不同数据类型

        newInstance(参数) 调用构造函数创建对象

        setAccessible(boolean) 取消私有权限修饰符限制

3、获取成员变量:

4、获取成员方法:

24、lambda 表达式和枚举

1、Lambda

Lambda 是一个匿名函数,我们可以把Lambda 表达式理解为是一段可以传递的代码(将代码像数据一样进行传递)。可以写出更简洁、更灵活的代码。作为一种更紧凑的代码风格,使Java的语言表达能力得到了提升。

java内置的函数式接口:

1、断言式接口:(传入指定数据类型的一个参数,判断其对错)

public interface Predict{

boolean test(T t);

}

public class Test1 {
    public static void main(String[] args) {
        boolean b = fun1(30, (e) -> {
            return e > 20;
        });
        System.out.println(b);//true
    }

    public static boolean fun1(int x, Predicate<Integer> predicate) {
        return predicate.test(x);
    }
}
2、函数型接口:(传入指定数据类型T的一个值,返回指定数据类型R的结果)

public interface Function(T R){

R apply(T t);

}

public class Test2 {
    public static void main(String[] args) {
        String s = fun1(100, integer -> {
            return integer + "%";
        });
        System.out.println(s); //100%
        System.out.println(s.getClass());//class java.lang.String
    }

    public static String fun1(int a, Function<Integer, String> function) {
        String apply = function.apply(a);
        return apply;
    }
}
3、供给型接口:(无参,返回指定数据类型数据)

public interface Supplier{

T get();

}

public class Test3 {
    public static void main(String[] args) {
        String fun = fun(() -> {
            return "123";
        });
        System.out.println(fun); //123
    }

    public static String fun(Supplier<String> supplier) {
        return supplier.get();
    }
}
4、消费型接口:(传入指定类型数据,无返回值)

public interface Consumer{

void accept(T t);

}

public class Test4 {
    public static void main(String[] args) {
        fun((e) -> {
            System.out.println(e.equals("hello world!")); //true
        });
    }

    public static void fun(Consumer<String> consumer) {
        consumer.accept("hello world!");
    }
}

2、枚举:

1. 将枚举相关的对象放在开头 SPRING("春天", "春暖花开")

2. 创建枚举类的属性(成员遍历),必须是作为私有常量出现

3. 必须将构造方法私有化,这是为了保证类的对象是有限个的目的

4. 提供公共的获取属性的方法

5. 重写toString()方法

public class EnumTest1 {
    public static void main(String[] args) {
        Enum1 spring = Enum1.SPRING;
        System.out.println(spring); //Enum1{name='春', information='万物复苏'}
        System.out.println(spring.getName() + " => " + spring.getInformation());//春 => 万物复苏
        System.out.println(Arrays.toString(Enum1.values()));//返回全部枚举,保存到数组中
//[Enum1{name='春', information='万物复苏'}, Enum1{name='夏', information='烈日炎炎'}, Enum1{name='秋', information='秋高气爽'}, Enum1{name='冬', information='白雪皑皑'}]
        System.out.println(Enum1.valueOf("SUMMER"));//Enum1{name='夏', information='烈日炎炎'}
        System.out.println(spring.name());//SPRING
    }
}

enum Enum1 {
    //春天是spring;夏天是summer;秋天是autumn;冬天是winter。
    //1. 将枚举相关的对象放在开头
    SPRING("春", "万物复苏"),
    SUMMER("夏", "烈日炎炎"),
    AUTUMN("秋", "秋高气爽"),
    WINTER("冬", "白雪皑皑");

    //2. 创建枚举类的属性(成员遍历),必须是作为私有常量出现
    private String name;
    private String information;

    //3. 必须将构造方法私有化,这是为了保证类的对象是有限个的目的
    private Enum1() {}
    
    private Enum1(String name, String information) {
        this.name = name;
        this.information = information;
    }

    //4. 提供公共的获取属性的方法
    public String getName() {return name;}

    public String getInformation() {return information;}

    //5. 重写toString()方法
    @Override
    public String toString() {
        return "Enum1{" +
                "name='" + name + '\'' +
                ", information='" + information + '\'' +
                '}';
    }
}

3、枚举类常用方法

        1、name:返回枚举类的名字

        2、toString:不重写和name一样。重写后根据重写输出

        3、value:获取所有的枚举对象,返回到一个数组中

        4、valueOf( str ):将字符串包装成枚举类型,不是枚举类中的字符串报错

以上一节代码为例:

//name
Enum1.SPRING.name();//返回SPRING
Enum1.SUMMER.name();//返回SUMMER

//toString
Enum1.SUMMER.toString();//返回Enum1{name='春天',information='万物复苏'}

//value
Enum1.value();//返回一个数组,保存每一个枚举对象

//valueOf(str)
Enum1.valueOf("SPRING");//返回一个Enum1.SPRING枚举对象

4、枚举实现接口

public class EnumTest1 {
    public static void main(String[] args) {
        Enum1.SPRING.describe();//春天万物复苏
        Enum1.SUMMER.describe();//夏天烈日炎炎
        Enum1.AUTUMN.describe();//秋天秋高气爽
        Enum1.WINTER.describe();//冬天白雪皑皑
    }
}

interface Desc {
    void describe();
}

//每个单独的枚举都能单独实现接口中的同一个方法
enum Enum1 implements Desc {
    //春天是spring;夏天是summer;秋天是autumn;冬天是winter。
    SPRING("春", "万物复苏") {
        @Override
        public void describe() {
            System.out.println("春天万物复苏");
        }
    },
    SUMMER("夏", "烈日炎炎") {
        @Override
        public void describe() {
            System.out.println("夏天烈日炎炎");
        }
    },
    AUTUMN("秋", "秋高气爽") {
        @Override
        public void describe() {
            System.out.println("秋天秋高气爽");
        }
    },
    WINTER("冬", "白雪皑皑") {
        @Override
        public void describe() {
            System.out.println("冬天白雪皑皑");
        }
    };

    private String name;
    private String information;

    private Enum1() {}

    private Enum1(String name, String information) {
        this.name = name;
        this.information = information;
    }

    public String getName() {return name;}

    public String getInformation() {return information;}

    @Override
    public String toString() {
        return "Enum1{" +
                "name='" + name + '\'' +
                ", information='" + information + '\'' +
                '}';
    }
}

25、动态代理

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

// 定义接口
interface UserService {
    void addUser(String username);
}

// 实现接口的具体类
class UserServiceImpl implements UserService {
    public void addUser(String username) {
        System.out.println("添加用户:" + username);
    }
}

// 实现InvocationHandler接口
class MyInvocationHandler implements InvocationHandler {
    private Object target;

    public MyInvocationHandler(Object target) {
        this.target = target;
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("动态代理前置操作");
        Object result = method.invoke(target, args);
        System.out.println("动态代理后置操作");
        return result;
    }
}

public class DynamicProxyExample {
    public static void main(String[] args) {
        // 创建目标对象
        UserService userService = new UserServiceImpl();

        // 创建InvocationHandler实例
        MyInvocationHandler handler = new MyInvocationHandler(userService);

        // 创建动态代理对象
        UserService proxy = (UserService) Proxy.newProxyInstance(
                userService.getClass().getClassLoader(),
                userService.getClass().getInterfaces(),
                handler
        );

        // 通过代理对象调用方法
        proxy.addUser("Alice");
    }
}

//输出结果
    动态代理前置操作
    添加用户:Alice
    动态代理后置操作

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值