Java基础_综合笔记

Java基础_综合笔记

cmd

  • 常用cmd命令:
    • 盘符+冒号 E: 进入相应的盘
    • dir:查看当前路径的内容
    • cd目录:打开单击目录
    • cd… :回到上层目录
    • cd 目录1\目录2: 进入多级目录
    • cd\ :退回到盘符根目录
    • cls:清屏
    • exit:退出cmd窗口
  • 环境变量
    • 在环境变量中的系统环境变量中的Path属性中可以加安装软件的完整地址,这样不管在哪个目录下打开此软件,计算机都可以通过环境变量打开。
    • 本地编辑java程序
      • javac 文件名.java
      • java 文件名
    • Java环境变量
      • 在系统环境变量中新建名为JAVA_HOME的变量,里面存放jdk安装路径如:E:\jdk8
      • 在path中添加新的路径为**%JAVA_HOME%**

Java可以干什么

  • JavaSE:Java语言(标准版),用于桌面应用的开发,是其它两个版本的基础

    • 桌面应用:用户只要打开程序,程序界面会让用户在短的时间内找到他们需要的功能,同时主动带领用户完成他们的工作并得到最好的体验如:电脑自带的计算器(不占优势,一般为C或C++)
  • JavaEE(企业版): 用于Web分享的网站开发,(占优势)

    • 网站开发:浏览器+服务器
  • Java跨平台的原因

    • Java语言是混合型的计算机语言,运行一个Java程序大致可分为以下3步骤
    1. 程序员编写Java程序
    2. 将Java程序编译成后缀名为.class的字节码文件
    3. 机器执行字节码文件,执行程序的运行。

    ​ Java可以支持多平台的运行,程序员写了一套代码放在不同的操作系统上可以执行原因是因为Java程序是由Java虚拟机运行的,因此只需要在不同平台安装好对应的虚拟机就可以运行Java程序了。

  • JDK和JRE

    • JDK是什么,有哪些部分组成
      jdk是Java开发工具包里面包含:
      • JVM虚拟机:Java运行的地方
      • 核心类库:java写好的API,我们可以直接使用
      • 开发工具:Java、Javac、jdb、。。。。。。
    • JRE是什么,由哪些部分组成
      • JRE是运行环境
      • 里面包含JVM,核心类库,运行工具

    jdk包含了jre包含了jvm


Java基础

  • java中的注释

    • 单行注释

      // 这是一个单行注释
      
    • 多行注释

      /*这是一个多行注释*/
      
    • 文档注释

      /**
      这是一个文档注释
      */
      
  • 关键字

    • 什么是关键字
      被Java赋予一定含义的英文单词,Java中有五十多个关键字

    • 关键字的特点
      全部由小写字母组成

      常用的代码编辑器中,关键字会以高亮的形式展现

    • class关键字是什么意思
      class是定义一个类的关键字,实例代码

      public class HelloWorld{
        //上面这对大括号里就是HelloWorld这个类的范围
        public static void main(String[] args){
          
        }
      }
      
  • 字面量

    • 什么是字面量
      告诉Java程序员:数据在程序中的书写格式

    • Java字面量的分类

      整数、小数、字符串、字符、布尔、null

    • 一些特殊字面量的书写

      制表符’\t’ :表示补齐字符为8位长度

  • 变量

    • 变量的定义格式
      • 数据类型 变量名 = 数据值; 如: int a = 10;
    • 使用变量
      • 输出打印 参与计算 修改记录的值
    • 使用场景
      • 重复使用某个值
      • 某个数据经常发生变化
    • 注意事项
      • 变量只能存储一个值
      • 变量名不能重复
      • 一条语句可以定义多个变量
      • 使用前一定要赋值
  • 进制
    java中可使用二进制、八进制、十进制、十六进制

    前缀0:八进制 前缀0x十六进制 前缀0b二进制 没有前缀十进制

    • 任意进制转十进制

      • 系数*基数的权次幂相加
        系数:就是每一位的树

        基数:就是几进制

        权: 从右往左,依次为0.1.2.3.4.5…

    • 十进制转任意数:

      • 除基取余法
        用十进制的数除以要转进制的基数,取余数,一直除到商为0,从下往上取余数倒着拼接。
  • 基本数据类型

    Java中基本数据类型有八种分别是:

    • 整数:byte(-128到127) short int long

    • 浮点数:float double

    • 字符型:char

    • 布尔类型 boolean

      long定义变量时需要加L或l float定义变量时需要加F或f

  • 标识符

    Java中的标识符包含各种名称,如变量名,方法名,类名等

    • 硬性规定:
      标识符命名只能以字母、下划线、美元符$、数字组成,不能与关键字重名,不能以数字开头。

    • 软性规定
      一般定义变量名和方法名时,使用小驼峰命名法:首个单词全部小写,有多个单词的话从第二个单词开始,每个单词首字母大写。如:firstName

      大驼峰命名法:一般用在类名的定义上:所有单词首字母大写。如:FirstName

  • 键盘录用

    java中获取键盘输入的值是使用Scanner这个包,步骤如下

    1. 导入Scanner包:import java.util.Scanner;(需要在类名上面导入)
    2. 在类中的main方法创建Scanner输入对象: Scanner sc = new Scanner(System.in);
    3. 定义接收键盘的变量: int i = sc.nextInt();
  • 运算符

    • 算数运算符

      + - * / %     加 减 乘 除 取余
      
      算数运算符在进行不同类型的数据时,数据会统一转换成一致的类型再运算,默认转换规则是按照取值范围从小到大。也可进行手动强转。(数据类型)变量名。
      
      byte short char 这三个类型的数据在进行运算时会自动升级到int类型
      含有字符串的+代表拼接字符串
      
    • 自增自减运算符

      n++ n--  ++n --n   后缀表执行完这行语句在加1    前缀代表先加1再执行语句
      
    • 赋值运算符

      = += -= *= /= %=   
      
    • 关系运算符

      关系运算符也叫比较运算符,就是将运算符左边的值和右边的值进行比较。

      符号解释
      ==就是判断左边跟右边是否相等,如果成立就是true,如果不成立就是false
      !=就是判断左边跟右边是否不相等,如果成立就是true,如果不成立就是false
      >就是判断左边是否大于右边,如果成立就是true,如果不成立就是fals
      >=就是判断左边是否大于等于右边,如果成立就是true,如果不成立就是false
      <就是判断左边是否小于右边,如果成立就是true,如果不成立就是false
      <=就是判断左边是否小于等于右边,如果成立就是true,如果不成立就是false

      关系运算符的结果一定是布尔类型

    • 逻辑运算符

      • &:逻辑与

        ​ 两边都为真,结果就为真,一边为假,就为假。

      • |:逻辑或
        ​ 一边为真,结果就为真

      • ^:异或:如果两边相同,结果为false,如果两边不同,结果为true

      • !:逻辑非,取反
        false取反就是true,true取反就是false

      • &&:短路逻辑与 ||:逻辑或

        ​ 当左边不能确定整个表达式的结果,右边才会执行。

        ​ 当左边能确定整个表达式的结果,那么右边就不会执行了。从而提高了代码的运行效率。

    • 三元运算符

      • 格式:

        关系表达式 ? 值1 : 值2

        当关系表达式成立是,为值1;不成立时,为值2

    • 运算符优先级

      带有小括号()的最优先。


Java基础之流程控制语句

  • 流程控制语句分类

    1. 顺序结构
    2. 判断与选择结构(if,switch)
    3. 循环结构(for,while,do while)
  • 顺序结构

    顺序结构是程序中最简单最基本的流程控制,没有特定的语法结构,按照代码的先后顺序,依次执行,程序中大多数的代码都是这样执行的。

  • if,else语句

    格式:

    if(表达式1){
      语句体1
    }
    //当表达式1成立就执行语句体1.若不成立则跳过if语句。
    
    if(表达式2{
      语句体2
    }else{
      语句体3
    }
    //当表达式2成立,则执行语句体2,若不成立则执行语句体3.
    
    if(表达式1){
      语句体1
    }else if(表达式2{
      语句体2
    }else{
      语句体3
    }
    //当表达式1成立,则执行语句体1,若不成立,判断表达式2是否成立,若成立,运行语句体2,若还不成立,运行语句体3
    
  • switch语句
    格式:

    switch(表达式){
        case1:
          语句体1breakcase2:
          语句体2break;
        case3:
          语句体3breakcase4:
          语句体4breakdefault:
    	  语句5break}
    

    先运行表达式,用表达式的结果与case后面的值匹配,然后执行对应的语句体,若都没有对应到,则执行default下的语句体。break代表跳出这个switch语句,若不加的话,会将下面的语句体都执行一遍。default可以不加。


Java基础之循环

  • for循环

    循环格式:

    for (初始化语句;条件判断语句;条件控制语句) {
    	循环体语句;
    }
    

    格式解释:

    • 初始化语句: 用于表示循环开启时的起始状态,简单说就是循环开始的时候什么样
    • 条件判断语句:用于表示循环反复执行的条件,简单说就是判断循环是否能一直执行下去
    • 循环体语句: 用于表示循环反复执行的内容,简单说就是循环反复执行的事情
    • 条件控制语句:用于表示循环执行中每次变化的内容,简单说就是控制循环是否能执行下去

    执行流程:

    ①执行初始化语句

    ②执行条件判断语句,看其结果是true还是false

    ​ 如果是false,循环结束

    ​ 如果是true,继续执行

    ③执行循环体语句

    ④执行条件控制语句

    ⑤回到②继续

  • while循环

    循环格式:

    初始化语句;
    while(条件判断语句){
    	循环体;
    	条件控制语句;
    }
    
  • do while循环

    循环格式:

    初始化语句;
    do{
        循环体;
        条件控制语句;
    }while(条件判断语句);
    
    
    • 三种循环的区别:
      for和while循环,是先判断,再执行。

      ​do…while是先执行,再判断。

      ​当知道循环次数或者循环范围的时候,用for循环。

      ​当不知道循环次数,也不知道循环范围,但是知道循环的结束条件时,用while循环。

  • 条件控制语句

    • break
      不能单独存在,必须配合switch或者循环使用,当程序执行到break后会跳出当前循环

    • continue

      不能单独存在的。只能存在于循环当中。表示:跳过本次循环,继续执行下次循环。

  • 随机数Random

    • 使用步骤:

      1. 导包
      import java.util.Random;
      导包的动作必须出现在类定义的上边。
      
      1. 创建对象
      Random r = new Random ();
      上面这个格式里面,只有r是变量名,可以变,其他的都不允许变。
      
      1. 生成随机数
      int number = r.nextInt(随机数的范围);
      上面这个格式里面,只有number是变量名,可以变,其他的都不允许变。
      随机数范围的特点:从0开始,不包含指定值。比如:参数为10,生成的范围[0,10)
      

      代码示例:

      //1.导包
      import java.util.Random;
      
      public class RandomDemo1 {
          public static void main(String[] args) {
              //2.创建对象
              Random r = new Random();
              //3.生成随机数
              int number = r.nextInt(100);//包左不包右,包头不包尾
              //0 ~ 99
              System.out.println(number);
      
          }
      }
      

Java基础之数组

  • 概念

    就是一种容器,可以用来存放多个同种数据类型的值

  • 数组的定义:

    1. 数据类型[] 数组名 例如: int[] arr;
    2. 数据类型 数组名[] 例如: int arr[];
  • 数组的静态初始化

    • 完整格式:
      数据类型[] 数组名 = new 数据类型[]{元素1,元素2,元素3,元素4…};
    • 简化格式:
      数据类型[] 数组名 = {元素1,元素2,元素3,元素4…};
  • 数组元素访问

    • 格式:
      数组名[索引]

      索引:从0开始,表示数组从左往右的元素

    • 数组的长度:
      数组名.length;

  • 数组的动态初始化

    • 格式:
      数据类型[] 数组名 = new 数据类型[数组的长度];

java基础之方法

  • **概念:**方法(method)是程序中最小的执行单元,方法分为无参方法和有参方法

    • 注意:
      • 方法必须先创建才可以使用,该过程成为方法定义
      • 方法创建后并不是直接可以运行的,需要手动使用后,才执行,该过程成为方法调用
  • 无参方法定义格式

    public static void 方法名 (   ) {
    	// 方法体;
    }
    
    
    • 调用方法

      方法名();
      
  • 有参方法的定义
    参数:由数据类型和变量名组成 - 数据类型 变量名

    public static void 方法名 (参数1) {
    	方法体;
    }
    
    public static void 方法名 (参数1, 参数2, 参数3...) {
    	方法体;
    }
    
    • 调用方法:
      传入的参数必须和定义方法时参数的类型一致

      方法名(参数);
      
      方法名(参数1,参数2);
      
  • 形参和实参

    1. 形参:方法定义中的参数

    ​ 等同于变量定义格式,例如:int number

    1. 实参:方法调用中的参数
  • 带返回值方法定义和调用

    • 定义格式:
      方法定义时return后面的返回值与方法定义上的数据类型要匹配,否则程序将报错

      public static 数据类型 方法名 ( 参数 ) { 
      	return 数据 ;
      }
      
    • 调用格式

      方法名 ( 参数 ) ;
      数据类型 变量名 = 方法名 ( 参数 ) ;
      
  • 方法的重载

    • 概念:方法重载指同一个类中定义的多个方法之间的关系,满足下列条件的多个方法相互构成重载

      • 多个方法在同一个类中
      • 多个方法具有相同的方法名
      • 多个方法的参数不相同,类型不同或者数量不同
      • 重载仅对应方法的定义,与方法的调用无关,调用方式参照标准格式
      • 重载仅针对同一个类中方法的名称与参数进行识别,与返回值无关,换句话说不能通过返回值来判定两个方法是否相互构成重载

面向对象

客观存在的事物皆为对象 ,所以我们也常常说万物皆对象。

    • 类的理解
      • 类是对现实生活中一类具有共同属性和行为的事物的抽象
      • 类是对象的数据类型,类是具有相同属性和行为的一组对象的集合
      • 简单理解:类就是对现实事物的一种描述
    • 类的组成
      • 属性:指事物的特征,例如:手机事物(品牌,价格,尺寸)
      • 行为:指事物能执行的操作,例如:手机事物(打电话,发短信)
  • 类和对象的关系
    • 类:类是对现实生活中一类具有共同属性和行为的事物的抽象
    • 对象:是能够看得到摸的着的真实存在的实体
    • 简单理解:类是对事物的一种描述,对象则为具体存在的事物

类的定义步骤

  1. 定义类

  2. 编写类的成员变量

  3. 编写类的成员方法

    public class 类名 {
    	// 成员变量
    	变量1的数据类型 变量1;
    	变量2的数据类型 变量2;// 成员方法
    	方法1;
    	方法2;	
    }
    
  • 对象的使用

    • 创建对象的格式
      • 类名 对象名 = new 类名();
    • 调用对象的格式
      • 对象名.成员变量
      • 对象名.成员方法();
  • 成员变量和局部变量

    • 类中位置不同:成员变量(类中方法外)局部变量(方法内部或方法声明上)
    • 内存中位置不同:成员变量(堆内存)局部变量(栈内存)
    • 生命周期不同:成员变量(随着对象的存在而存在,随着对象的消失而消失)局部变量(随着方法的调用而存在,醉着方法的调用完毕而消失)
    • 初始化值不同:成员变量(有默认初始化值)局部变量(没有默认初始化值,必须先定义,赋值才能使用)
  • 封装

    1. 封装概述
      是面向对象三大特征之一(封装,继承,多态)

      对象代表什么,就得封装对应的数据,并提供数据对应的行为

    2. 封装代码实现
      将类的某些信息隐藏在类内部,不允许外部程序直接访问,而是通过该类提供的方法来实现对隐藏信息的操作和访问
      成员变量private,提供对应的getXxx()/setXxx()方法

  • private关键字

    private是一个修饰符,代表私有的,可以用来修饰成员(成员变量,成员方法)

    • 被private修饰的成员,只能在本类进行访问,针对private修饰的成员变量,如果需要被其他类使用,提供相应的操作
      • 提供“get变量名()”方法,用于获取成员变量的值,方法用public修饰
      • 提供“set变量名(参数)”方法,用于设置成员变量的值,方法用public修饰
  • this关键字

    • this修饰的变量用于指代成员变量,其主要作用是(区分局部变量和成员变量的重名问题)
    • 方法的形参如果与成员变量同名,不带this修饰的变量指的是形参,而不是成员变量
    • 方法的形参没有与成员变量同名,不带this修饰的变量指的是成员变量
  • 构造方法
    构造方法是用来创建对象的方法
    创建对象: Student stu = new Student();

    • 格式:

      public class 类名{
      
              修饰符 类名( 参数 ) {
      
              }
      
      }
      
  • 构造方法的注意事项

    • 构造方法的创建

    如果没有定义构造方法,系统将给出一个默认的无参数构造方法
    如果定义了构造方法,系统将不再提供默认的构造方法

    • 构造方法的重载

    如果自定义了带参构造方法,还要使用无参数构造方法,就必须再写一个无参数构造方法

    • 推荐的使用方式

    无论是否使用,都手工书写无参数构造方法

    • 重要功能!

    可以使用带参构造,为成员变量进行初始化


字符串

  • String类概述

    ​ String 类代表字符串,Java 程序中的所有字符串文字(例如“abc”)都被实现为此类的实例。也就是说,Java 程序中所有的双引号字符串,都是 String 类的对象。String 类在 java.lang 包下,所以使用的时候不需要导包!

  • String类特点

    • 字符串不可变,它们的值在创建后不能被更改
    • 虽然 String 的值是不可变的,但是它们可以被共享
    • 字符串效果上相当于字符数组( char[] ),但是底层原理是字节数组( byte[] )
  • String类构造方法常用的构造方法

    方法名说明
    public String()创建一个空白字符串对象,不含有任何内容
    public String(char[] chs)根据字符数组的内容,来创建字符串对象
    public String(byte[] bys)根据字节数组的内容,来创建字符串对象
    String s = “abc”;直接赋值的方式创建字符串对象,内容就是abc
  • 创建字符串对象两种方式的区别

    • 通过构造方法创建

      ​通过 new 创建的字符串对象,每一次 new 都会申请一个内存空间,虽然内容相同,但是地址值不同

    • 直接赋值方式创建

      ​以“”方式给出的字符串,只要字符序列相同(顺序和大小写),无论在程序代码中出现几次,JVM 都只会建立一个 String 对象,并在字符串池中维护

  • 字符串的比较

    • ==号的作用

      • 比较基本数据类型:比较的是具体的值
      • 比较引用数据类型:比较的是对象地址值
    • equals方法的作用

      • 方法介绍
      public boolean equals(String s)     比较两个字符串内容是否相同、区分大小写
      

  • StringBuilder

    • StringBuilder 可以看成是一个容器,创建之后里面的内容是可变的。

      当我们在拼接字符串和反转字符串的时候会使用到

    基本使用

    public class StringBuilderDemo3 {
        public static void main(String[] args) {
            //1.创建对象
            StringBuilder sb = new StringBuilder("abc");
    
            //2.添加元素
            /*sb.append(1);
            sb.append(2.3);
            sb.append(true);*/
    
            //反转
            sb.reverse();
    
            //获取长度
            int len = sb.length();
            System.out.println(len);
    
        //打印
        //普及:
        //因为StringBuilder是Java已经写好的类
        //java在底层对他做了一些特殊处理。
        //打印对象不是地址值而是属性值。
        System.out.println(sb);
    }
    

    }

    
    - 链式编程
    
      ```java
      public class StringBuilderDemo4 {
          public static void main(String[] args) {
              //1.创建对象
              StringBuilder sb = new StringBuilder();
    
              //2.添加字符串
              sb.append("aaa").append("bbb").append("ccc").append("ddd");
    
              System.out.println(sb);//aaabbbcccddd
    
              //3.再把StringBuilder变回字符串
              String str = sb.toString();
              System.out.println(str);//aaabbbcccddd
    
          }
      }
    

ArrayList

  • 概述

    • 什么是集合

      ​ 提供一种存储空间可变的存储模型,存储的数据容量可以发生改变

    • ArrayList集合的特点

      ​ 长度可以变化,只能存储引用数据类型。

    • 泛型的使用

      ​ 用于约束集合中存储元素的数据类型

  • 与数组的区别

    1. 长度可变
    2. 添加数据的时候不需要考虑索引,默认将数据添加到末尾
  • ArrayList常用方法

    • 构造方法:

      方法名说明
      public ArrayList()创建一个空的集合对象
    • 成员方法:

      方法名说明
      public boolean add(要添加的元素)将指定的元素追加到此集合的末尾
      public boolean remove(要删除的元素)删除指定元素,返回值表示是否删除成功
      public E remove(int index)删除指定索引处的元素,返回被删除的元素
      public E set(int index,E element)修改指定索引处的元素,返回被修改的元素
      public E get(int index)返回指定索引处的元素
      public int size()返回集合中的元素的个数

Static关键字

一些类的定义

public class Student {
    // 成员变量
    public String name;
    public char sex; // '男'  '女'
    public int age;

    // 无参数构造方法
    public Student() {

    }
    
    // 有参数构造方法
    public Student(String  a) {

    }
}

我们已经知道面向对象中,存在类和对象的概念,我们在类中定义了一些成员变量,例如name,age,sex ,结果发现这些成员变量,每个对象都存在(因为每个对象都可以访问)。

而像name ,age , sex确实是每个学生对象都应该有的属性,应该属于每个对象。

所以Java中成员(变量和方法)等是存在所属性的,Java是通过static关键字来区分的。static关键字在Java开发非常的重要,对于理解面向对象非常关键。

关于 static 关键字的使用,它可以用来修饰的成员变量和成员方法,被static修饰的成员是属于类的是放在静态区中,没有static修饰的成员变量和方法则是属于对象的。我们上面案例中的成员变量都是没有static修饰的,所以属于每个对象。

  • 静态变量及其访问
    有static修饰成员变量,说明这个成员变量是属于类的,这个成员变量称为类变量或者静态成员变量。 直接用 类名访问即可。因为类只有一个,所以静态成员变量在内存区域中也只存在一份。所有的对象都可以共享这个变量。
    定义格式

    修饰符 static 数据类型 变量名 = 初始值; 
    

    举例

    public class Student {
        public static String schoolName = "传智播客"// 属于类,只有一份。
        // .....
    }
    

    静态成员变量的访问:

    格式:类名.静态变量

    public static void  main(String[] args){
        System.out.println(Student.schoolName); // 传智播客
        Student.schoolName = "黑马程序员";
        System.out.println(Student.schoolName); // 黑马程序员
    }
    
  • 静态方法及其访问

    有static修饰成员方法,说明这个成员方法是属于类的,这个成员方法称为类方法或者静态方法**。 直接用 类名访问即可。因为类只有一个,所以静态方法在内存区域中也只存在一份。所有的对象都可以共享这个方法。

    与静态成员变量一样,静态方法也是直接通过类名.方法名称即可访问。

    举例

    public class Student{
        public static String schoolName = "传智播客"// 属于类,只有一份。
        // .....
        public static void study(){
        	System.out.println("我们都在黑马程序员学习");   
        }
    }
    

    静态成员变量的访问:

    格式:类名.静态方法

    public static void  main(String[] args){
        Student.study();
    }
    
  • 小结

    1.当 static 修饰成员变量或者成员方法时,该变量称为静态变量,该方法称为静态方法。该类的每个对象都共享同一个类的静态变量和静态方法。任何对象都可以更改该静态变量的值或者访问静态方法。但是不推荐这种方式去访问。因为静态变量或者静态方法直接通过类名访问即可,完全没有必要用对象去访问。

    2.无static修饰的成员变量或者成员方法,称为实例变量,实例方法,实例变量和实例方法必须创建类的对象,然后通过对象来访问。

    3.static修饰的成员属于类,会存储在静态区,是随着类的加载而加载的,且只加载一次,所以只有一份,节省内存。存储于一块固定的内存区域(静态区),所以,可以直接被类名调用。它优先于对象存在,所以,可以被所有对象共享。

    4.无static修饰的成员,是属于对象,对象有多少个,他们就会出现多少份。所以必须由对象调用。


继承

  • 继承概述

    继承描述的是事物之间的所属关系,这种关系是:is-a 的关系。例如,兔子属于食草动物,食草动物属于动物。可见,父类更通用,子类更具体。我们通过继承,可以使多种事物之间形成一种关系体系。

    继承:就是子类继承父类的属性行为,使得子类对象可以直接具有与父类相同的属性、相同的行为。子类可以直接访问父类中的非私有的属性和行为。

  • 继承的好处

    1. 提高代码的复用性(减少代码冗余,相同代码重复利用)。
    2. 使类与类之间产生了关系
  • 继承的格式

    通过extends关键字继承以声明一个子类继承另外一个父类,定义格式如下:

    class 父类 {
    	...
    }
    
    class 子类 extends 父类 {
    	...
    }
    

    需要注意:Java是单继承的,一个类只能继承一个直接父类,跟现实世界很像,但是Java中的子类是更加强大的。

  • super关键字

    当子类继承父类后,成员变量名若是不一样,在调用变量的时候会先在子类中寻找,若没有则到父类中寻找。要是子类中有变量名与父类中的一样,那么在调用该变量名的时候会优先使用子类的变量。super关键字就是让调用的时候使用父类的成员变量

    • 使用方法:

      super.父类成员变量名
      
  • 方法的重写

    继承后的特征之一就是方法重写,方法重写值的是“当子类继承父类后,可以在子类中定义与父类相同的方法(返回类型,方法名,参数类型和参数变量名都一样)但方法体却不一样。”这样,不同的子类在调用重写后的方法时可以实现不同的效果。子类方法覆盖父类方法,必须要保证权限大于等于父类权限。

    • @Override:注解,重写注解校验!

    • 这个注解标记的方法,就说明这个方法必须是重写父类的方法,否则编译阶段报错。

    • 建议重写都加上这个注解,一方面可以提高代码的可读性,一方面可以防止重写出错!

      加上后的子类代码形式如下

  • 继承后的构造方法
    子类不能继承父类的构造方法

    子类的构造方法中默认有一个super() ,表示调用父类的构造方法,父类成员变量初始化后,才可以给子类使用。(先有爸爸,才能有儿子

    继承后子类构方法器特点:子类所有构造方法的第一行都会默认先调用父类的无参构造方法

  • 小结

    • 子类的每个构造方法中均有默认的super(),调用父类的空参构造。手动调用父类构造会覆盖默认的super()。
    • super() 和 this() 都必须是在构造方法的第一行,所以不能同时出现。
    • super(…)和this(…)是根据参数去确定调用父类哪个构造方法的。
    • super(…)可以调用父类构造方法初始化继承自父类的成员变量的数据。
    • this(…)可以调用本类中的其他构造方法。

多态

多态是继封装、继承之后,面向对象的第三大特性。

多态是出现在继承或者实现关系中的

多态体现的格式

父类类型 变量名 = new 子类/实现类构造器;
变量名.方法名();

多态的前提:有继承关系,子类对象是可以赋值给父类类型的变量。例如Animal是一个动物类型,而Cat是一个猫类型。Cat继承了Animal,Cat对象也是Animal类型,自然可以赋值给父类类型的变量。

  • 多态的使用场景

    • 当一个方法的形参是一个类,我们可以传递这个类所有的子类对象。
    • 当一个方法的形参是一个接口,我们可以传递这个接口所有的实现类对象(后面会讲)。
    • 而且多态还可以根据传递的不同对象来调用不同类中的方法。
  • 多态的定义和前提
    多态时指同一行为,具有多个不同的表现形式

    • 前提:
      1. 有继承或者实现关系
      2. 方法的重写【意义体现:不重写,无意义】
      3. 父类引用指向子类对象【格式体现】
  • 多态的运行特点

    ​ 调用成员变量时:编译看左边,运行看左边

    ​ 调用成员方法时:编译看左边,运行看右边
    代码示例

    Fu f = new Zi()//编译看左边的父类中有没有name这个属性,没有就报错
    //在实际运行的时候,把父类name属性的值打印出来
    System.out.println(f.name);
    //编译看左边的父类中有没有show这个方法,没有就报错
    //在实际运行的时候,运行的是子类中的show方法
    f.show();
    
  • 多态的缺点
    我们已经知道多态编译阶段是看左边父类类型的,如果子类有些独有的功能,此时多态的写法就无法访问子类独有功能了

  • 多态下的自动类型转换

    • 向上转型
      就是用父类构造子类的实例对象
      多态本身是子类类型向父类类型向上转换(自动转换)的过程,这个过程是默认的。
      当父类引用指向一个子类对象时,便是向上转型。

      父类类型  变量名 = new 子类类型();
      如:Animal a = new Cat(); 
      
    • 向下转型
      就是将父类强制赋值给子类

      父类类型向子类类型向下转换的过程,这个过程是强制的。
      一个已经向上转型的子类对象,将父类引用转为子类引用,可以使用强制类型转换的格式,便是向下转型。

      子类类型 变量名 = (子类类型) 父类变量名;:Aniaml a = new Cat();
         Cat c =(Cat) a;  
      
  • instanceof关键字

    为了避免ClassCastException的发生,Java提供了 instanceof 关键字,给引用变量做类型的校验,格式如下:

    变量名 instanceof 数据类型 
    如果变量属于该数据类型或者其子类类型,返回true。
    如果变量不属于该数据类型或者其子类类型,返回false

    所以,转换前,我们最好先做一个判断,代码如下:

    public class Test {
        public static void main(String[] args) {
            // 向上转型  
            Animal a = new Cat();  
            a.eat();               // 调用的是 Cat 的 eat
    
            // 向下转型  
            if (a instanceof Cat){
                Cat c = (Cat)a;       
                c.catchMouse();        // 调用的是 Cat 的 catchMouse
            } else if (a instanceof Dog){
                Dog d = (Dog)a;       
                d.watchHouse();       // 调用的是 Dog 的 watchHouse
            }
        }  
    }
    

权限修饰符

在Java中提供了四种访问权限,使用不同的访问权限修饰符修饰时,被修饰的内容会有不同的访问权限,我们之前已经学习过了public 和 private,接下来我们研究一下protected和默认修饰符的作用。

  • public:公共的,所有地方都可以访问。

  • protected:本类 ,本包,其他包中的子类都可以访问。

  • 默认(没有修饰符):本类 ,本包可以访问。

    注意:默认是空着不写,不是default

  • private:私有的,当前类可以访问。
    public > protected > 默认 > private

  • 不同权限的访问能力

publicprotected默认private
同一类中
同一包中的类
不同包的子类
不同包中的无关类

可见,public具有最大权限。private则是最小权限。

编写代码时,如果没有特殊的考虑,建议这样使用权限:

  • 成员变量使用private ,隐藏细节。
  • 构造方法使用 public ,方便创建对象。
  • 成员方法使用public ,方便调用方法。

小贴士:不加权限修饰符,就是默认权限


final关键字

  • 概述
    在Java中,final表示不可变,它是一个修饰符,被它所修饰的内容表示不可改变。

    • final: 不可改变,最终的含义。可以用于修饰类、方法和变量。

    • 类:被修饰的类,不能被继承。

    • 方法:被修饰的方法,不能被重写。

    • 变量:被修饰的变量,有且仅能被赋值一次。

      被final修饰的常量名称,一般都有书写规范,所有字母都大写


抽象类

​ 我们把没有方法体的方法称为抽象方法。Java语法规定,包含抽象方法的类就是抽象类。

  • 抽象方法 : 没有方法体的方法。

  • 抽象类:包含抽象方法的类。

  • abstract使用格式

    abstract是抽象的意思,用于修饰方法方法和类,修饰的方法是抽象方法,修饰的类是抽象类。

  • 抽象方法
    使用abstract 关键字修饰方法,该方法就成了抽象方法,抽象方法只包含一个方法名,而没有方法体。

    定义格式:

    修饰符 abstract 返回值类型 方法名 (参数列表)

    代码举例:

    public abstract void run()
  • 抽象类

如果一个类包含抽象方法,那么该类必须是抽象类。注意:抽象类不一定有抽象方法,但是有抽象方法的类必须定义成抽象类。

定义格式:

abstract class 类名字 { 
  
}

代码举例:

public abstract class Animal {
    public abstract void run()}
  • 抽象类的特征

    抽象类的特征总结起来可以说是 有得有失

    有得:抽象类得到了拥有抽象方法的能力。

    有失:抽象类失去了创建对象的能力。

    其他成员(构造方法,实例方法,静态方法等)抽象类都是具备的

  • 抽象类的意义
    抽象类存在的意义是为了被子类继承,否则抽象类将毫无意义。抽象类可以强制让子类,一定要按照规定的格式进行重写。


接口

  • 概述
    接口是更加彻底的抽象,JDK7之前,包括JDK7,接口中全部是抽象方法。接口同样是不能创建对象的。-

  • 定义格式

    //接口的定义格式:
    interface 接口名称{
        // 抽象方法
    }
    
    // 接口的声明:interface
    // 接口名称:首字母大写,满足“驼峰模式”
    
    
  • 接口中的抽象方法
    注意:接口中的抽象方法默认会自动加上public abstract修饰程序员无需自己手写!!

  • 接口中的常量
    在接口中定义的成员变量默认会加上: public static final修饰。也就是说在接口中定义的成员变量实际上是一个常量。这里是使用public static final修饰后,变量值就不可被修改,并且是静态化的变量可以直接用接口名访问,所以也叫常量。常量必须要给初始值。常量命名规范建议字母全部大写,多个单词用下划线连接

  • 接口的实现
    类与接口的关系为实现关系,即类实现接口,该类可以称为接口的实现类,也可以称为接口的子类。实现的动作类似继承,格式相仿,只是关键字不同,实现使用 implements关键字。

    • 实现接口的格式:

      /**接口的实现:
          在Java中接口是被实现的,实现接口的类称为实现类。
          实现类的格式:*/
      class 类名 implements 接口1,接口2,接口3...{
      
      }
      
  • 类实现接口的要求与意义:

    1. 必须重写实现的全部接口中所有抽象方法。
    2. 如果一个类实现了接口,但是没有重写完全部接口的全部抽象方法,这个类也必须定义成抽象类。
    3. 意义:接口体现的是一种规范,接口对实现类是一种强制性的约束,要么全部完成接口申明的功能,要么自己也定义成抽象类。这正是一种强制性的规范
  • 接口的实现与继承
    接口与接口之间可以多继承的
    一个类也可以实现多个接口

内部类

按位置来分,内部类可分为以下几种:

  1. 成员内部内,类定义在了成员位置 (类中方法外称为成员位置,无static修饰的内部类)
  2. 静态内部类,类定义在了成员位置 (类中方法外称为成员位置,有static修饰的内部类)
  3. 局部内部类,类定义在方法内
  4. 匿名内部类,没有名字的内部类,可以在方法中,也可以在类中方法外。
  • 成员内部类

    • 无static修饰的内部类,属于外部类对象的。

    • 宿主:外部类对象。

    • 使用格式:

      外部类.内部类。 // 访问内部类的类型都是用 外部类.内部类
      

      获取成员内部类对象的两种方式

      方式一:外部直接创建成员内部类的对象

      外部类.内部类 变量 = new 外部类().new 内部类();
      

      方式二:在外部类中定义一个方法提供内部类的对象

  • 静态内部类

    • 静态内部类是一种特殊的成员内部类。

    • 有static修饰,属于外部类本身的。

    • 总结:静态内部类与其他类的用法完全一样。只是访问的时候需要加上外部类.内部类。

    • 拓展1:静态内部类可以直接访问外部类的静态成员。

    • 拓展2:静态内部类不可以直接访问外部类的非静态成员,如果要访问需要创建外部类的对象。

    • 拓展3:静态内部类中没有银行的Outer.this。

    内部类的使用格式

    外部类.内部类。
    

    静态内部类对象的创建格式

    外部类.内部类  变量 = new  外部类.内部类构造器;
    

    调用方法的格式:

    • 调用非静态方法的格式:先创建对象,用对象调用
    • 调用静态方法的格式:外部类名.内部类名.方法名();

    案例演示

    // 外部类:Outer01
    class Outer01{
        private static  String sc_name = "黑马程序";
        // 内部类: Inner01
        public static class Inner01{
            // 这里面的东西与类是完全一样的。
            private String name;
            public Inner01(String name) {
                this.name = name;
            }
            public void showName(){
                System.out.println(this.name);
                // 拓展:静态内部类可以直接访问外部类的静态成员。
                System.out.println(sc_name);
            }
        }
    }
    
    public class InnerClassDemo01 {
        public static void main(String[] args) {
            // 创建静态内部类对象。
            // 外部类.内部类  变量 = new  外部类.内部类构造器;
            Outer01.Inner01 in  = new Outer01.Inner01("张三");
            in.showName();
        }
    }
    
  • 局部内部类
    定义在方法中的类。定义格式:

    class 外部类名 {
    	数据类型 变量名;
    	
    	修饰符 返回值类型 方法名(参数列表) {
    		// …
    		class 内部类 {
    			// 成员变量
    			// 成员方法
    		}
    	}
    }
    
  • *匿名内部类

    匿名内部类 :是内部类的简化写法。他是一个隐含了名字的内部类。开发中,最常用到的内部类就是匿名内部类了。

    • 格式
    new 类名或者接口名() {
         重写方法;
    };
    

    包含了:

    • 继承或者实现关系
    • 方法重写
    • 创建对象

    所以从语法上来讲,这个整体其实是匿名内部类对象

    • 什么时候用到匿名内部类

    实际上,如果我们希望定义一个只要使用一次的类,就可考虑使用匿名内部类。匿名内部类的本质作用

    是为了简化代码

    之前我们使用接口时,似乎得做如下几步操作:

    1. 定义子类
    2. 重写接口中的方法
    3. 创建子类对象
    4. 调用重写后的方法
    interface Swim {
        public abstract void swimming();
    }
    
    // 1. 定义接口的实现类
    class Student implements Swim {
        // 2. 重写抽象方法
        @Override
        public void swimming() {
            System.out.println("狗刨式...");
        }
    }
    
    public class Test {
        public static void main(String[] args) {
            // 3. 创建实现类对象
            Student s = new Student();
            // 4. 调用方法
            s.swimming();
        }
    }
    

    我们的目的,最终只是为了调用方法,那么能不能简化一下,把以上四步合成一步呢?匿名内部类就是做这样的快捷方式。

    • 匿名内部类的使用方式与前提

    匿名内部类必须继承一个父类或者实现一个父接口

    匿名内部类格式

    new 父类名或者接口名(){
        // 方法重写
        @Override 
        public void method() {
            // 执行语句
        }
    };
    
    • 匿名内部类使用方式

    以接口为例,匿名内部类的使用,代码如下:

    interface Swim {
        public abstract void swimming();
    }
    
    public class Demo07 {
        public static void main(String[] args) {
            // 使用匿名内部类
    		new Swim() {
    			@Override
    			public void swimming() {
    				System.out.println("自由泳...");
    			}
    		}.swimming();
    
            // 接口 变量 = new 实现类(); // 多态,走子类的重写方法
            Swim s2 = new Swim() {
                @Override
                public void swimming() {
                    System.out.println("蛙泳...");
                }
            };
    
            s2.swimming();
            s2.swimming();
        }
    }
    
    • 匿名内部类特点
    1. 定义一个没有名字的内部类
    2. 这个类实现了父类,或者父类接口
    3. 匿名内部类会创建这个没有名字的类的对象

常用API

  • 包装类

    • 基本包装类

      将基本数据类型封装成对象的好处在于可以在对象中定义更多的功能方法操作该数据常用的操作之一:用于基本数据类型与字符串之间的转换

    • 基本类型对应的包装类

      基本数据类型包装类
      byteByte
      shortShort
      intInteger
      longLong
      floatFloat
      doubleDouble
      charCharacter
      booleanBoolean
  • Interger类

    包装一个对象中的原始类型int的值

    • Interger类构造方法:

      方法名说明
      public Integer(int value)根据 int 值创建 Integer 对象(过时)
      public Integer(String s)根据 String 值创建 Integer 对象(过时)
      public static Integer valueOf(int i)返回表示指定的 int 值的 Integer 实例
      public static Integer valueOf(String s)返回一个保存指定值的 Integer 对象 String
    • int和String类型的相互转换int转换为String

      转换方式

      方式一:直接在数字后加一个空字符串

      方式二:通过String类静态方法valueOf()

    • String转换为int

      转换方式

      方式一:先将字符串数字转成Integer,再调用valueOf()方法

      方式二:通过Integer静态方法parseInt()进行转换

    • 自动拆箱和自动装箱(理解)

      自动装箱

      把基本数据类型转换为对应的包装类类型

      自动拆箱

      把包装类类型转换为对应的基本数据类型


  • Date类

    • Date类概述:

      Date 代表了一个特定的时间,精确到毫秒

    • Date类的构造方法

      方法名说明
      public Date()分配一个 Date对象,并初始化,以便它代表它被分配的时间,精确到毫秒
      public Date(long date)分配一个 Date对象,并将其初始化为表示从标准基准时间起指定的毫秒数
    • Date类的常用方法

      方法名说明
      public long getTime()获取的是日期对象从1970年1月1日 00:00:00到现在的毫秒值
      public void setTime(long time)设置时间,给的是毫秒值
  • SimpleDateFormat类

    • SimpleDateFormat概述:

      SimpleDateFormat是一个具体的类,用于以区域设置敏感的方式格式化和解析日期。

    SimpleDateFormat的构造方法

    方法名说明
    public SimpleDateFormat()构造一个SimpleDateFormat,使用默认模式和日期格式
    public SimpleDateFormat(String pattern)构造一个SimpleDateFormat使用给定的模式和默认的日期 格式
    • SimpleDateFormat类的常用方法

      • 格式化(从Date到String)
      • public fifinal String format(Date date):将日期格式化成日期/时间字符串
    • 解析(从String到Date)

      • public Date parse(String source):从给定字符串的开始解析文本以生成日期
    • 实例代码:

      public class SimpleDateFormatDemo {
          public static void main(String[] args) throws ParseException {
              //格式化:从 Date 到 String
            Date d = new Date(); 
              // SimpleDateFormat sdf = new SimpleDateFormat(); 
              SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");
              String s = sdf.format(d);
              System.out.println(s);
              System.out.println("--------");
      
              //从 String 到 Date 
            String ss = "2048-08-09 11:11:11";
              //ParseException
            SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
              Date dd = sdf2.parse(ss);
              System.out.println(dd);
          }
      }
      
      

  • Calendar类

    • 概述:
      Calendar 为特定瞬间与一组日历字段之间的转换提供了一些方法,并为操作日历字段提供了一些方法

      Calendar 提供了一个类方法 getInstance 用于获取这种类型的一般有用的对象。

      该方法返回一个Calendar 对象。

      其日历字段已使用当前日期和时间初始化:Calendar rightNow = Calendar.getInstance();

    • Calendar常用方法:

      方法说明
      public int get(int fifield)返回给定日历字段的值
      public abstract void add(int fifield, int amount)根据日历的规则,将指定的时间量添加或减去给定的日
      public fifinal void set(int year,int month,int date)设置当前日历的年月日

Java进阶

异常

  • 概述

    异常就是程序出现了不正常的情况

  • 异常的体系结构

    在这里插入图片描述

    • Error:严重问题,不需要处理
    • Exception:称为异常类,它表示程序本省可以处理的问题
      • RuntimeException:在编译期间不检查,出现问题后,需要我们回来修改代码
      • 非RuntimeException:编译期就需要处理的,否指程序不能通过编译,更不能正常运行
  • JVM默认处理异常的方式

    如果程序出现了问题,我们没有做任何处理,最终JVM 会做默认的处理,处理方式有如下两个步骤:

    1. 把异常的名称,错误原因及异常出现的位置等信息输出在了控制台
    2. 程序停止执行
  • try-catch方式处理异常

    • 定义格式

      trt{
        可能出现异常的代码块
      }catch(异常类名 变量名){
        异常的处理代码
      }
      
    • 执行流程

      • 程序从 try 里面的代码开始执行
      • 出现异常,就会跳转到对应的 catch 里面去执行
      • 执行完毕之后,程序还可以继续往下执行
  • Throwable成员方法

    • 常用方法:

      方法名说明
      public String getMessage()返回此 throwable 的详细消息字符串
      public String toString()返回此可抛出的简短描述
      public void printStackTrace()把异常的错误信息输出在控制台
  • 编译时异常和运行时异常的区别

    • 编译时异常

      ​ 都是Exception类及其子类

      ​ 必须显示处理,否则程序就会发生错误,无法通过编译

    • 运行时异常

      ​ 都是RuntimeException类及其子类

      ​ 无需显示处理,也可以和编译时异常一样处理

  • throws方法处理异常

    • 定义格式:

      public void 方法() throws 异常类名 { }
      
  • throws与throw区别

    区别一:throw 是语句抛出一个异常;throws 是方法抛出一个异常;

    区别二:throws可以单独使用,但throw不能;

    区别三:throw要么和try-catch-finally语句配套使用,要么与throws配套使用。但throws可以单独使用,然后再由处理异常的方法捕获。


Collection集合

  • 集合体系结构

    提供一种存储空间可变的存储模型,存储的数据容量可以随时发生改变

  • 集合的体系

    Collection:

    • List: ArrayList、LinkedList
    • Set:HashSet、TreeSet

    Map:

    • HashMap

    其中Collection、List、Set、Map都为接口。ArrayList、LinkedList、HashSet、TreeSet、HashMap为对应的实现类。

  • Collection集合的概述
    此接口是单例集合的顶层接口,它表示一组对象,这些对象也称为Collection的元素。JDK不提供此接口任何直接实现,它提供更具体的子接口实现。

  • Collection的常用方法

    方法名说明
    boolean add(E e)添加元素
    boolean remove(Object o)从集合中移除指定的元素
    void clear()清空集合中的元素
    boolean contains(Object o)判断集合中是否存在指定的元素
    boolean isEmpty()判断集合是否为空
    int size()集合的长度,也就是集合中元素的个数
  • Collection集合的遍历

    1. 迭代器,集合的专用遍历方式

      Iterator iterator():返回此集合中元素的迭代器,通过集合的iterator()方法得到

      迭代器是通过集合的iterator()方法得到的,所以我们说它是依赖于集合而存在的

      • 迭代器案例:

        public class IteratorDemo {
          public static void main(String[] args) {
            //创建集合对象
            Collection<String> c = new ArrayList<>();
            //添加元素
            c.add("hello"); 
            c.add("world");
            c.add("java");
            c.add("javaee");
             //Iterator<E> iterator():返回此集合中元素的迭代器,通过集合的iterator()方法得到 
            Iterator<String> it = c.iterator();
            //用while循环改进元素的判断和获取 
            while (it.hasNext()) { 
              String s = it.next(); 
              System.out.println(s);
            }
          }
        }
        

        这个案例中,it为集合生成的迭代器对象,在while循环中,hasNext()为集合中的下一个元素,若有的话就为真。next()方法为放回集合中的元素。


List集合

  • 概述:

    Lsit为有序集合(也称为序列),用户可以精确控制列表中每个元素的插入位置。用户可以通过整数索引访问元

    素,并搜索列表中的元素

    与Set集合不同,列表通常允许重复的元素

  • 特点:
    有索引

    可以存储重复元素

    元素存取有序

  • List集合的特有方法

    方法名说明
    void add(int index,E element)在此集合中的指定位置插入指定的元素
    E remove(int index)删除指定索引处的元素,返回被删除的元素
    E set(int index,E element)修改指定索引处的元素,返回被修改的元素
    E get(int index)返回指定索引处的元素

    这些是List集合的特有方法,Conllection集合的方法list也可使用

  • List集合的遍历

    public class ListDemo{
      public static void main(String[] args){
        //创建List集合
        List<String> list = new List<String>;
        //增加对象
         list.add("aaa");
         list.add("bbb");
         list.add("ccc");
    	//迭代器方式遍历
        Iterator<Student> it = list.iterator();
        while(it.hasNext()){
          String s = it.next();
          System.out.println(s);
        }
        
        //for循环遍历:
        for(int i = 0;i < list.size();i++){
          s = list.get(i);
          System.out.println(s);
        }
      }
    }
    
  • List列表迭代器

    通过List集合的listIterator()方法得到,所以说它是List集合特有的迭代器

    用于允许程序员沿任一方向遍历的列表迭代器,在迭代期间修改列表,并获取列表中迭代器的当前位置

    • 代码演示:

      public class ListIteratorDemo {
        public static void main(String[] args) {
          //创建集合对象
          List<String> list = new ArrayList<String>();
          //添加元素 
          list.add("hello"); 
          list.add("world"); 
          list.add("java");
          //获取列表迭代器 
          ListIterator<String> lit = list.listIterator();
          while (lit.hasNext()) {
            String s = lit.next();
            if(s.equals("world")) { 
              lit.add("javaee"); 
            }
          }
          System.out.println(list); 
        } 
      } 
      
    • listIterator()主要是为了解决普通迭代器iterator()在循环遍历时集合中的元素进行增删改时会引发的并发修改异常问题ConcurrentModifificationException,也可以使集合倒序遍历。

  • 增强for循环
    定义格式:

    for(元素数据类型 变量名 : 数组/集合对象名) 
    { 
      循环体;
    }
    

    其中变量名就是每个集合中的元素。内部原理是一个Iterator迭代器

  • List集合中的数据结构

    • 数据结构之栈和队列
      栈结构

      • 先进后出

      队列结构

      • 先进先出
    • 数据结构之数组和链表

      数组结构

      • 增删慢,查询块

      队列结构

      • 增删块,查询慢
  • List集合子类的特点
    ArrayList

    底层是数组结构实现,查询快、增删慢

    LinkedList

    底层是链表结构实现,查询慢、增删快

  • LinkedList的特有方法

    方法名说明
    public void addFirst(E e)在该列表开头插入指定的元素
    public void addLast(E e)将指定的元素追加到此列表的末尾
    public E getFirst()返回此列表中的第一个元素
    public E getLast()返回此列表中的最后一个元素
    public E removeFirst()从此列表中删除并返回第一个元素
    public E removeLast()从此列表中删除并返回最后一个元素

Set集合

  • Set集合的特点

    元素存取无序

    没有索引、只能通过迭代器或增强for循环遍历

    不能存储重复元素

  • 哈希值

    哈希值简介

    ​ 是JDK根据对象的地址或者字符串或者数字算出来的int类型的数值

    如何获取哈希值

    ​ Object类中的public int hashCode():返回对象的哈希码值

    哈希值的特点

    ​ 同一个对象多次调用hashCode()方法返回的哈希值是相同的

    ​ 默认情况下,不同对象的哈希值是不同的。而重写hashCode()方法,可以实现让不同对象的哈希值相同

  • HashSet集合概述与特点

    • 底层数据结构是哈希表
    • 对集合的迭代顺序不作任何保证,也就是说不保证存储和取出的元素顺序一致
    • 没有带索引的方法,所以不能使用普通for循环遍历,可以用for增强遍历
    • 由于是Set集合,所以是不包含重复元素的集合
  • LinkedHashSet概述与特点

    • 哈希表和链表实现的Set接口,具有可预测的迭代次序
    • 由链表保证元素有序,也就是说元素的存储和取出顺序是一致的
    • 由哈希表保证元素唯一,也就是说没有重复的元素

Set集合排序
  • TreeSet集合概述与特点

    • 元素有序,可以按照一定的规则进行排序,具体排序方式取决于构造方法
      • TreeSet():根据其元素的自然排序进行排序
      • TreeSet(Comparator comparator) :根据指定的比较器进行排序
    • 没有带索引的方法,所以不能使用普通for循环遍历
    • 由于是Set集合,所以不包含重复元素的集合

    代码演示:

    public class TreeSetDemo01 { 
      public static void main(String[] args) {
        //创建集合对象
        TreeSet<Integer> ts = new TreeSet<Integer>(); 
        //添加元素
        ts.add(10);
        ts.add(40); 
        ts.add(30); 
        ts.add(50);
        ts.add(20);
        ts.add(30);
        //遍历集合
        for(Integer i : ts) {
          System.out.println(i); 
        }
      } 
    }
    
  • 自然排序Comparable的使用

    使用TreeSet无参构造方法创建的TreeSet集合默认就是使用Comparable进行自然排序

  • 比较器排序Comparator的使用
    这个排序比较器是在创建TreeSet集合时在TreeSet参数位置加一个Conparator的匿名内部类,在其内部重写compare方法。在方法内设置要用元素的哪个属性进行排序;

    代码演示:

    TreeSet<Student> ts = new TreeSet<Student>(new Comparator<Student> () {
      @Override public int compare(Student s1, Student s2) {
        //this.age - s.age 
        //s1,s2 
        int num = s1.getAge() - s2.getAge();
        int num2 = num == 0 ? s1.getName().compareTo(s2.getName()) : num;
        return num2; }
    });
                                      
    

泛型

  • 泛型的概述

    JDK5中引入的特性,它提供了编译时类型安全检测机制,该机制允许在编译时检测到非法的类型

    它的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。一提到参数,最熟悉的就是定义方

    法时有形参,然后调用此方法时传递实参。那么参数化类型怎么理解呢?顾名思义,就是将类型由原来的具

    体的类型参数化,然后在使用/调用时传入具体的类型。这种参数类型可以用在类、方法和接口中,分别被称

    为泛型类、泛型方法、泛型接口。

  • 泛型的定义格式

    <类型>:指定一种类型的格式。这里的类型可以看成是形参

    <类型1,类型2…>:指定多种类型的格式,多种类型之间用逗号隔开。这里的类型可以看成是形参

    将来具体调用时候给定的类型可以看成是实参,并且实参的类型只能是引用数据类型

  • 泛型的好处

    把运行时期的问题提前到了编译期间

    避免了强制类型转换

  • 泛型类演示

    • 定义泛型类:
      定义格式:

      修饰符 class 类名<类型> { }
      
      public class Generic<T>{
        public set(T t){
          this.t = t;
        }
        public get(T t){
          return t;
        }
      }
      
      
    • 使用泛型类:

      public class GenericDemo {
          public static void main(String[] args) {
              Generic<String> g1 = new Generic<String>();
              g1.setT("林青霞");
              System.out.println(g1.getT());
      
              Generic<Integer> g2 = new Generic<Integer>();
              g2.setT(30);
              System.out.println(g2.getT());
      
              Generic<Boolean> g3 = new Generic<Boolean>();
              g3.setT(true);
              System.out.println(g3.getT());
          }
      }
      
      
  • 泛型方法
    定义格式:

    修饰符 <类型> 返回值类型 方法名(类型 变量名) { }
    
    public class Generic {
        public <T> void show(T t) {
            System.out.println(t);
        }
    }
    
    
    • 使用泛型方法:

      public class GenericDemo {
          public static void main(String[] args) {
              Generic g = new Generic();
              g.show("林青霞");
              g.show(30);
              g.show(true);
              g.show(12.34);
          }
      }
      
  • 泛型接口
    定义格式:

    修饰符 interface 接口名<类型> { }
    
    public interface Generic<T> {
        void show(T t);
    }
    

    泛型接口实现类

    public class GenericImpl<T> implements Generic<T> {
        @Override
        public void show(T t) {
            System.out.println(t);
        }
    }
    
    
    • 使用类

      public class GenericDemo {
          public static void main(String[] args) {
              Generic<String> g1 = new GenericImpl<String>();
              g1.show("林青霞");
      
              Generic<Integer> g2 = new GenericImpl<Integer>();
              g2.show(30);
          }
      }
      
      

  • 类型通配符

    • 类型通配符的作用
      • 为了表示各种泛型List的父类,可以使用类型通配符
    • 类型通配符的分类
      • 类型通配符:<?>
    • List<?>:表示元素类型未知的List,它的元素可以匹配任何的类型
      • 这种带通配符的List仅表示它是各种泛型List的父类,并不能把元素添加到其中
    • 类型通配符上限:<? extends 类型>
      • List<? extends Number>:它表示的类型是Number或者其子类型
    • 类型通配符下限:<? super 类型>
      • List<? super Number>:它表示的类型是Number或者其父类型
  • 类型通配符的基本使用

    public class GenericDemo {
      public static void main(String[] args) {
        //类型通配符:<?>
        List<?> list1 = new ArrayList<Object>();
        List<?> list2 = new ArrayList<Number>();
        List<?> list3 = new ArrayList<Integer>();
        System.out.println("--------"); 
        //类型通配符上限:<? extends 类型> 
        //  List<? extends Number> list4 = new ArrayList<Object>();
        List<? extends Number> list5 = new ArrayList<Number>();
        List<? extends Number> list6 = new ArrayList<Integer>();
        System.out.println("--------"); 
        //类型通配符下限:<? super 类型>
        List<? super Number> list7 = new ArrayList<Object>(); 
        List<? super Number> list8 = new ArrayList<Number>(); 
        // List<? super Number> list9 = new ArrayList<Integer>();
      }
    }
    
  • 可变参数

    • 可变参数介绍

      • 可变参数又称参数个数可变,用作方法的形参出现,那么方法参数个数就是可变的了
    • 可变参数定义格式

      修饰符 返回值类型 方法名(数据类型… 变量名) { }
      
    • 可变参数的注意事项

      • 这里的变量其实是一个数组
      • 如果一个方法有多个参数,包含可变参数,可变参数要放在最后
    • 代码实例:

      public class ArgsDemo01 {
          public static void main(String[] args) {
              System.out.println(sum(10, 20));
              System.out.println(sum(10, 20, 30));
              System.out.println(sum(10, 20, 30, 40));
              System.out.println(sum(10, 20, 30, 40, 50));
              System.out.println(sum(10, 20, 30, 40, 50, 60));
              System.out.println(sum(10, 20, 30, 40, 50, 60, 70));
              System.out.println(sum(10, 20, 30, 40, 50, 60, 70, 80, 90, 100));
          }
      
          public static int sum(int... a) {
              int sum = 0;
      
              for (int i : a) {
                  sum += i;
              }
      
              return sum;
          }
      }
      
      
  • 可变参数的使用

    • Arrays工具类中有一个静态方法:
      • public static List asList(T… a):返回由指定数组支持的固定大小的列表
      • 返回的集合不能做增删操作,可以做修改操作
    • List接口中有一个静态方法:
      • public static List of(E… elements):返回包含任意数量元素的不可变列表
      • 返回的集合不能做增删改操作
    • Set接口中有一个静态方法:
      • public static Set of(E… elements) :返回一个包含任意数量元素的不可变集合
      • 在给元素的时候,不能给重复的元素
      • 返回的集合不能做增删操作,没有修改的方法

Map集合

  • 概述与特点

    Map集合是以键值对的形式来存储数据的格式如下:

    interface Map<K,V> K:键的类型;V:值的类型
    

    键值对映射关系

    一个键对应一个值

    键不能重复,值可以重复

    元素存取无序

  • Map集合的常用方法

    方法名说明
    V put(K key,V value)添加元素
    V remove(Object key)根据键删除键值对元素
    void clear()移除所有的键值对元素
    boolean containsKey(Object key)判断集合是否包含指定的键
    boolean containsValue(Object value)判断集合是否包含指定的值
    boolean isEmpty()判断集合是否为空
    int size()集合的长度,也就是集合中键值对的个数
  • Map集合获取方法

    方法名说明
    V get(Object key)根据键获取值
    Set keySet()获取所有键的集合
    Collection values()获取所有值的集合
    Set<Map.Entry<K,V>> entrySet()获取所有键值对对象的集合
  • Map集合的遍历

    1. 使用KeySet()方法

      代码演示:

      public class MapTest {
          public static void main(String[] args) {
              HashMap<String,String> hs = new HashMap();
              hs.put("aaa","张三");
              hs.put("bbb","李四");
              hs.put("ccc","王五");
              Set<String> set = hs.keySet();
              for(String s : set){
                  String o = hs.get(s);
                  System.out.println(o);
              }
          }
      }
      
    2. 使用 Set<Map.Entry<K,V>> entrySet()方法
      代码演示:

      public class MapTest {
          public static void main(String[] args) {
              HashMap<String,String> hs = new HashMap();
              hs.put("aaa","张三");
              hs.put("bbb","李四");
              hs.put("ccc","王五");
           
              Set<Map.Entry<String, String>> entries = hs.entrySet();
              for(Map.Entry<String,String>  entry : entries){
                  String key = entry.getKey();
                  String value = entry.getValue();
                  System.out.println(key+value);
              }
          }
      }
      

Collections集合工具类

  • 概述

Collection类是针对集合操作的工具类

  • 常用方法:

    方法名说明
    public static void sort(List list)将指定的列表按升序排序
    public static void reverse(List<?> list)反转指定列表中元素的顺序
    public static void shuffle(List<?> list)使用默认的随机源随机排列指定的列表

File类

  • Fiile类介绍

    • 它是文件和目录路径名的抽象表示

    • 文件和目录是可以通过File封装成对象的

    • 对于File而言,其封装的并不是一个真正存在的文件,仅仅是一个路径名而已。它可以是存在的,也可以

      是不存在的。将来是要通过具体的操作把这个路径的内容转换为具体存在的

  • File类的构造方法

    方法名说明
    File(String pathname)通过将给定的路径名字符串转换为抽象路径名来创建新的 File实例
    File(String parent, String child)从父路径名字符串和子路径名字符串创建新的 File实例
    File(File parent, String child)从父抽象路径名和子路径名字符串创建新的 File实例
    • 代码实例:

      public class FileDemo01 {
        public static void main(String[] args) {
          //File(String pathname):通过将给定的路径名字符串转换为抽象路径名来创建新的 File 实例。
          File f1 = new File("E:\\itcast\\java.txt");
          System.out.println(f1); 
          //File(String parent, String child):从父路径名字符串和子路径名字符串创建新的 File实例
          File f2 = new File("E:\\itcast","java.txt");
          System.out.println(f2); 
          //File(File parent, String child):从父抽象路径名和子路径名字符串创建新的 File 实例。 
          File f3 = new File("E:\\itcast");
          File f4 = new File(f3,"java.txt"); 
          System.out.println(f4); 
        }
      }
      
  • File创建功能

    方法名说明
    public boolean createNewFile()当具有该名称的文件不存在时,创建一个由该抽象路径名命名的新空文件
    public boolean mkdir()创建由此抽象路径名命名的目录
    public boolean mkdirs()创建由此抽象路径名命名的目录,包括任何必需但不存在的父目录
    • 代码实例:

      public class FileDemo02 {
        public static void main(String[] args) throws IOException {
          //需求1:我要在E:\\itcast目录下创建一个文件java.txt 
          File f1 = new File("E:\\itcast\\java.txt"); 
          System.out.println(f1.createNewFile());
          System.out.println("--------"); 
          //需求2:我要在E:\\itcast目录下创建一个目录JavaSE
          File f2 = new File("E:\\itcast\\JavaSE");
          System.out.println(f2.mkdir()); System.out.println("--------");
          //需求3:我要在E:\\itcast目录下创建一个多级目录JavaWEB\\HTML
          File f3 = new File("E:\\itcast\\JavaWEB\\HTML");
          System.out.println(f3.mkdirs());
          System.out.println("--------"); 
          //需求4:我要在E:\\itcast目录下创建一个文件javase.txt 
          File f4 = new File("E:\\itcast\\javase.txt");
          // System.out.println(f4.mkdir()); 
          System.out.println(f4.createNewFile());
        }
      }
      
  • File类判断和获取功能

    • 判断功能

      方法名说明
      public boolean isDirectory()测试此抽象路径名表示的File是否为目录
      public boolean isFile()测试此抽象路径名表示的File是否为文件
      public boolean exists()测试此抽象路径名表示的File是否存在
    • 获取功能

      方法名说明
      public String getAbsolutePath()返回此抽象路径名的绝对路径名字符串
      public String getPath()将此抽象路径名转换为路径名字符串
      public String getName()返回由此抽象路径名表示的文件或目录的名称
      public String[] list()返回此抽象路径名表示的目录中的文件和目录的名称字符串数组
      public File[] listFiles()返回此抽象路径名表示的目录中的文件和目录的File对象数组
  • File删除功能

    方法名说明
    public boolean delete()删除由此抽象路径名表示的文件或目录
  • 绝对路径和相对路径的区别

    • 绝对路径:完整的路径名,不需要任何其他信息就可以定位它所表示的文件。例如:E:\itcast\java.txt
    • 相对路径:必须使用取自其他路径名的信息进行解释。例如:myFile\java.txt

IO流

  • IO流介绍

    • IO:输入/输出(Input/Output)

    • 流:是一种抽象概念,是对数据传输的总称。也就是说数据在设备间的传输称为流,流的本质是数据传

    • IO流就是用来处理设备间数据传输问题的。常见的应用:文件复制;文件上传;文件下载

  • IO流的分类

    • 按照数据的流向

      输入流:读数据

      输出流:写数据

    • 按照数据类型来分

      字节流

      • 字节输入流
      • 字节输出流

      字符流

      • 字符输入流
      • 字符输出流
  • IO流的使用场景

    • 如果操作的是纯文本文件,优先使用字符流
    • 如果操作的是图片、视频、音频等二进制文件。优先使用字节流
    • 如果不确定文件类型,优先使用字节流。字节流是万能的流

字节流写数据
  • 字节流抽象基类

    • InputStream:这个抽象类是表示字节输入流的所有类的超类
    • OutputStream:这个抽象类是表示字节输出流的所有类的超类
    • 子类名特点:子类名称都是以其父类名作为子类名的后缀
  • 字节输出流

    • FileOutputStream(String name):创建文件输出流以指定的名称写入文件
  • 使用字节输出流写数据的步骤

    • 创建字节输出流对象(调用系统功能创建了文件,创建字节输出流对象,让字节输出流对象指向文件)
    • 调用字节输出流对象的写数据方法
    • 释放资源(关闭此文件输出流并释放与此流相关联的任何系统资源)
  • 代码实例:

    public class IOTest {
        public static void main(String[] args) throws IOException {
            FileOutputStream fos = new FileOutputStream("D:\\fos.txt");
            fos.write(2);//写入数据
            fos.write(23);
            fos.close();//释放资源
        }
    }
    
    

    这里的参数内的文件名要确实存在的。

  • 字节流写数据的三种方法

    • 写数据的方法分类

      方法名说明
      void write(int b)将指定的字节写入此文件输出流 一次写一个字节数据
      void write(byte[] b)将 b.length字节从指定的字节数组写入此文件输出流 一次写一个字节数组数据
      void write(byte[] b, int off, int len)将 len字节从指定的字节数组开始,从偏移量off开始写入此文件输出流 一 次写一个字节数组的部分数据
  • 字节流写数据实现换行
    不同系统格式不同:

    • windows:\r\n
    • linux:\n
    • mac:\r
  • 字节流写数据实现追加写入

    • public FileOutputStream(String name,boolean append)

      创建文件输出流以指定的名称写入文件。如果第二个参数为true ,则字节将写入文件的末尾而不是开头

    • 代码实例:

      public class FileOutputStreamDemo03 { 
        public static void main(String[] args) throws IOException {
          //创建字节输出流对象
          FileOutputStream fos = new FileOutputStream("myByteStream\\fos.txt",true); 
          //写数据
          for (int i = 0; i < 10; i++) {
            fos.write("hello".getBytes());
            fos.write("\r\n".getBytes()); }
          //释放资源
          fos.close(); 
        }
      
  • 字节流写数据时的异常处理

    • 异常处理格式

      • try-catch-finally
      try{
        可能出现异常的代码; 
      }
      catch(异常类名 变量名){
        异常的处理代码; 
      }finally{ 
        执行所有清除操作; 
      }
      

      finally特点

    被fifinally控制的语句一定会执行,除非JVM退出


字节流读数据
  • 字节输入流

    • FileInputStream(String name):通过打开与实际文件的连接来创建一个FileInputStream ,该文件由文

    件系统中的路径名name命名

  • 字节输入流读取数据的步骤

    1. 创建字节输入流对象
    2. 调用字节输入流对象的读数据方法
    3. 释放资源

    代码实例:

    public class FileInputStreamDemo01 {
      public static void main(String[] args) throws IOException { 
        //创建字节输入流对象 
        //
        FileInputStream(String name) FileInputStream fis = new FileInputStream("myByteStream\\fos.txt");
        int by;
        /* fis.read():读数据
        by=fis.read():把读取到的数据赋值给by 
        by != -1:判断读取到的数据是否是-1 */ 
        while ((by=fis.read())!=-1) {
          System.out.print((char)by);
        }
        //释放资源 
        fis.close();
      } 
    }
    
    
  • 字节流一次一个字节数组都数据

    方法:

    public int read(byte[] b):从输入流读取最多b.length个字节的数据

    返回的是读入缓冲区的总字节数,也就是实际的读取字节个数

代码实例:

public class IOTest {
    public static void main(String[] args) throws IOException {
        FileInputStream fis = new FileInputStream("C:\\1111111.docx");
        byte[] bytes = new byte[1024];
        int len;
        while ((len = fis.read(bytes))!=-1){
            System.out.println(new String(bytes,0,len));
        }
    fis.close();
    }
}

字节缓存冲流
  • 介绍

    • BufferOutputStream:该类实现缓冲输出流。 通过设置这样的输出流,应用程序可以向底层输出流写入字节,而不必为写入的每个字节导致底层系统的调用
    • BufferedInputStream:创建BufferedInputStream将创建一个内部缓冲区数组。 当从流中读取或跳过字节时,内部缓冲区将根据需要从所包含的输入流中重新填充,一次很多字节
  • 构造方法

    方法名说明
    BufferedOutputStream(OutputStream out)创建字节缓冲输出流对象
    BufferedInputStream(InputStream in)创建字节缓冲输入流对象

    读写数据和字节流一样。读写的书读比单纯使用字节流要快一点。


字符流
  • 字符流介绍

由于字节流操作中文不是特别的方便,所以Java就提供字符流

字符流 = 字节流 + 编码表

  • 字符串中编码解码相关方法

    方法名说明
    byte[] getBytes()使用平台的默认字符集将该 String编码为一系列字节
    byte[] getBytes(String charsetName)使用指定的字符集将该 String编码为一系列字节
    String(byte[] bytes)使用平台的默认字符集解码指定的字节数组来创建字符串
    String(byte[] bytes, String charsetName)通过指定的字符集解码指定的字节数组来创建字符串
  • 字符流中的编码解码问题

    • InputStreamReader:是从字节流到字符流的桥梁

      它读取字节,并使用指定的编码将其解码为字符

      它使用的字符集可以由名称指定,也可以被明确指定,或者可以接受平台的默认字符集

    • OutputStreamWriter:是从字符流到字节流的桥梁

      是从字符流到字节流的桥梁,使用指定的编码将写入的字符编码为字节

      它使用的字符集可以由名称指定,也可以被明确指定,或者可以接受平台的默认字符集

  • 构造方法

    方法名说明
    InputStreamReader(InputStream in)使用默认字符编码创建InputStreamReader对象
    InputStreamReader(InputStream in,String chatset)使用指定的字符编码创建InputStreamReader对象
    OutputStreamWriter(OutputStream out)使用默认字符编码创建OutputStreamWriter对象
    OutputStreamWriter(OutputStream out,String charset)使用指定的字符编码创OutputStreamWriter对象

    以上的形参要前缀File

  • 字符流写数据的五种方法

    方法名说明
    void write(int c)写一个字符
    void write(char[] cbuf)写入一个字符数组
    void write(char[] cbuf, int off int len)写入字符数组的一部分
    void write(String str)写一个字符串
    void write(String str, int offff, int len)写一个字符串的一部分
  • 刷新和关闭的方法

    方法名说明
    flush()刷新流,之后还可以继续写数据
    close()关闭流,释放资源,但是在关闭之前会先刷新流。一旦关闭,就不能再写数据
  • 字符流读数据的两种方法

    方法名说明
    int read()一次读一个字符数据
    int read(char[] cbuf)一次读一个字符数组数据

字符缓冲流
  • 介绍

    • BufferedWriter:将文本写入字符输出流,缓冲字符,以提供单个字符,数组和字符串的高效写入,可

      以指定缓冲区大小,或者可以接受默认大小。默认值足够大,可用于大多数用途

    • BufferedReader:从字符输入流读取文本,缓冲字符,以提供字符,数组和行的高效读取,可以指定缓

      冲区大小,或者可以使用默认大小。 默认值足够大,可用于大多数用途

  • 构造方法

    方法名说明
    BufferedWriter(Writer out)创建字符缓冲输出流对象
    BufferedReader(Reader in)创建字符缓冲输入流对象

    以上形参要前缀File。

  • 字符缓冲流的特有方法
    BufferedWriter:

    方法名说明
    void newLine()写一行行分隔符,行分隔符字符串由系统属性定义

    BufferedReader:

    方法名说明
    String readLine()读一行文字。 结果包含行的内容的字符串,不包括任何行终止字符如果流的结尾已经 到达,则为null

IO流小结
  • 字节流
    在这里插入图片描述

  • 字符流
    在这里插入图片描述


对象序列化流
  • 介绍

    • 对象序列化:就是将对象保存到磁盘中,或者在网络中传输对象

    • 这种机制就是使用一个字节序列表示一个对象,该字节序列包含:对象的类型、对象的数据和对象中存

      储的属性等信息

    • 字节序列写到文件之后,相当于文件中持久保存了一个对象的信息

    • 反之,该字节序列还可以从文件中读取回来,重构对象,对它进行反序列化

  • 对象序列化流:ObjectOutputStream

    • 将Java对象的原始数据类型和图形写入OutputStream。 可以使用ObjectInputStream读取(重构)对

    象。 可以通过使用流的文件来实现对象的持久存储。 如果流是网络套接字流,则可以在另一个主机上或

    另一个进程中重构对象

  • 构造方法

    方法名说明
    ObjectOutputStream(OutputStream out)创建一个写入指定的OutputStream的 ObjectOutputStream
  • 序列化对象的方法

    方法名说明
    void writeObject(Object obj)将指定的对象写入ObjectOutputStream

实例代码:

public class ObjectOutputStreamDemo {
  public static void main(String[] args) throws IOException { 
    //ObjectOutputStream(OutputStream out):创建一个写入指定的OutputStream 的ObjectOutputStream 
    ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("myOtherStream\\oos.txt"));
    //创建对象
    Student s = new Student("林青霞",30);
    //void writeObject(Object obj):将指定的对象写入ObjectOutputStream 
    oos.writeObject(s); 
    //释放资源 
    oos.close(); 
  } 
}

一个对象要想被序列化,该对象所属的类必须必须实现Serializable 接口,上面代码中的学生类实现了此接口

Serializable是一个标记接口,实现该接口,不需要重写任何方法

  • 对象反序列化流

  • ObjectInputStream

    • ObjectInputStream反序列化先前使用ObjectOutputStream编写的原始数据和对象
  • 构造方法

    方法名说明
    ObjectInputStream(InputStream in)创建从指定的InputStream读取的ObjectInputStream
  • 反序列化对象方法

    方法名说明
    Object readObject()从ObjectInputStream读取一个对象

实例代码

public class ObjectInputStreamDemo {
  public static void main(String[] args) throws IOException, ClassNotFoundException {
    //ObjectInputStream(InputStream in):创建从指定的InputStream读取的 ObjectInputStream 
    ObjectInputStream ois = new ObjectInputStream(new FileInputStream("myOtherStream\\oos.txt"));
    //Object readObject():从ObjectInputStream读取一个对象
    Object obj = ois.readObject();
    Student s = (Student) obj; 
    System.out.println(s.getName() + "," + s.getAge());
    ois.close(); 
  } 
}

  • serialVersionUID 和 transient

    • serialVersionUID

      • 用对象序列化流序列化了一个对象后,假如我们修改了对象所属的类文件,读取数据会不会出问题呢?
        • 会出问题,会抛出InvalidClassException异常
    • 如果出问题了,如何解决呢?

      • 重新序列化
      • 给对象所属的类加一个serialVersionUID
      • private static fifinal long serialVersionUID = 42L;
    • transient

      如果一个对象中的某个成员变量的值不想被序列化,又该如何实现呢?

      • 给该成员变量加transient关键字修饰,该关键字标记的成员变量不参与序列化过程

实例代码:

public class Student implements Serializable {
  private static final long serialVersionUID = 42L;
  private String name;
  private transient int age;
}

Properties集合
  • Properties介绍

    • 是一个Map体系的集合类
    • Properties可以保存到流中或从流中加载
    • 属性列表中的每个键及其对应的值都是一个字符串
  • Properties的基本使用

    public class PropertiesDemo01 {
      public static void main(String[] args) {
        //创建集合对象
        // Properties<String,String> prop = new Properties<String,String>(); //错 误
        Properties prop = new Properties(); 
        //存储元素
        prop.put("itheima001", "林青霞");
        prop.put("itheima002", "张曼玉"); 
        prop.put("itheima003", "王祖贤"); 
        //遍历集合 
        Set<Object> keySet = prop.keySet(); 
        for (Object key : keySet) { 
          Object value = prop.get(key); 
          System.out.println(key + "," + value);
        }
      } 
    }
    
  • Properties作为Map集合的特有方法

    方法名说明
    Object setProperty(String key,String value设置集合的键和值,都是String类型,底层调用 Hashtable方 法 put
    String getProperty(String key)使用此属性列表中指定的键搜索属性
    Set stringPropertyNames()从该属性列表中返回一个不可修改的键集,其中键及其对应的值是字符串
  • Properties和IO流相结合的方法

    方法名说明
    void load(InputStream inStream)从输入字节流读取属性列表(键和元素对)
    void load(Reader reader)从输入字符流读取属性列表(键和元素对)
    void store(OutputStream out, String comments)将此属性列表(键和元素对)写入此 Properties表中,以适合于使用 load(InputStream)方法的格式写入输出字节流
    void store(Writer writer, String comments)将此属性列表(键和元素对)写入此 Properties表中,以适合使用 load(Reader)方法的格式写入输出字符流

实例代码:

public class PropertiesDemo03 {
  public static void main(String[] args) throws IOException {
    //把集合中的数据保存到文件 
    // myStore(); 
    //把文件中的数据加载到集合
    myLoad(); 
  }
   //把文件中的数据加载到集合
  private static void myLoad() throws IOException {
    Properties prop = new Properties(); 
    //void load(Reader reader):
    FileReader fr = new FileReader("myOtherStream\\fw.txt"); 
    prop.load(fr);
    fr.close();
    System.out.println(prop);
  }
    //把集合中的数据保存到文件 
  private static void myStore() throws IOException { 
    Properties prop = new Properties();
    prop.setProperty("itheima001","林青霞");
    prop.setProperty("itheima002","张曼玉");
    prop.setProperty("itheima003","王祖贤");
    //void store(Writer writer, String comments): F
    ileWriter fw = new FileWriter("myOtherStream\\fw.txt");
    prop.store(fw,null);
    fw.close(); 
  }
}

多线程

  • 进程和线程

    • 进程:是正在运行的程序

      是系统进行资源分配和调用的独立单位

      每一个进程都有它自己的内存空间和系统资源

    • 线程:是进程中的单个顺序控制流,是一条执行路径

      单线程:一个进程如果只有一条执行路径,则称为单线程程序

      多线程:一个进程如果有多条执行路径,则称为多线程程序

实现多线程方式一:继承Thread类
  • 方法介绍:

    方法名说明
    void run()在线程开启后,此方法将被调用执行
    void start()使此线程开始执行,Java虚拟机会调用run方法()
  • 实现步骤

    1. 定义一个类MyThread继承Thread类
    2. 在MyThread类中重写run()方法
    3. 创建MyThread类的对象
    4. 启动线程
  • 代码实例

    public class MyThread extends Thread {
      
      @Override public void run() {
        for(int i=0; i<100; i++) {
          System.out.println(i);
        }
      } 
    }
    public class MyThreadDemo { 
      public static void main(String[] args) {
        MyThread my1 = new MyThread();
        MyThread my2 = new MyThread(); 
        // my1.run();
        // my2.run(); 
        //void start() 导致此线程开始执行; Java虚拟机调用此线程的run方法 my1.start();
    
    	my2.start(); 
      }
    }
    
  • 为什么要重写run()方法
    因为run()是用来封装被线程执行的方法。

  • run()方法和star()方法的区别
    run():封装线程执行的代码,直接调用,相当于普通方法的调用

    start():启动线程;然后由JVM调用此线程的run()方法

  • 设置和获取线程名称

    方法名说明
    void setName(String name)将此线程的名称更改为等于参数name
    String getName()返回此线程的名称
    Thread currentThread()返回对当前正在执行的线程对象的引用
  • 线程的优先级

    • 两种调度方式

      • 分时调度模型:所有线程轮流使用 CPU 的使用权,平均分配每个线程占用 CPU 的时间片
      • 抢占式调度模型:优先让优先级高的线程使用 CPU,如果线程的优先级相同,那么会随机选择一

      个,优先级高的线程获取的 CPU 时间片相对多一些

    • Java使用的是抢占式调度模型

    • 随机性

      假如计算机只有一个 CPU,那么 CPU 在某一个时刻只能执行一条指令,线程只有得到CPU时间片,也

      就是使用权,才可以执行指令。所以说多线程程序的执行是有随机性,因为谁抢到CPU的使用权是不一

      定的

  • 优先级相关方法

    方法名说明
    fifinal int getPriority()返回此线程的优先级
    fifinal void setPriority(int newPriority)更改此线程的优先级 线程默认优先级是5;线程优先级的范围:1-10
  • 线程控制

    方法名说明
    static void sleep(long millis)使当前正在执行的线程停留(暂停执行)指定的毫秒数
    void join()等待这个线程死亡
    void setDaemon (boolean on)将此线程标记为守护线程,当运行的线程都是守护线程时,Java虚拟机 将退出
  • 线程的生命周期
    线程一共有五种状态,线程在各种状态之间转换。
    在这里插入图片描述


实现多线程方式二:实现Runnable接口
  • Thread构造方法

    方法名说明
    Thread(Runnable target)分配一个新的Thread对象
    Thread(Runnable target, String name)分配一个新的Thread对象
  • 实现步骤

    1. 定义一个类MyRunnable实现Runnable接口
    2. 在MyRunnable类中重写run()方法
    3. 创建MyRunnable类的对象
    4. 创建Thread类的对象,把MyRunnable对象作为构造方法的参数
    5. 启动线程
  • 代码实例:

    public class MyRunnable implements Runnable {
      @Override 
      public void run() {
        for(int i=0; i<100; i++) { 
          System.out.println(Thread.currentThread().getName()+":"+i);
        } 
      } 
    }
    public class MyRunnableDemo { 
      public static void main(String[] args) { 
        //创建MyRunnable类的对象 
        MyRunnable my = new MyRunnable();
        //创建Thread类的对象,把MyRunnable对象作为构造方法的参数
        //Thread(Runnable target) 
         Thread t1 = new Thread(my); 
         Thread t2 = new Thread(my); 
        //Thread(Runnable target, String name) 
        Thread t1 = new Thread(my,"高铁");
        Thread t2 = new Thread(my,"飞机"); 
        //启动线程 
        t1.start(); 
        t2.start();
      } 
    }
    
  • 多线程的实现方案有两种

    • 继承Thread类
    • 实现Runnable接口
  • 相比继承Thread类,实现Runnable接口的好处

    • 避免了Java单继承的局限性

    • 适合多个相同程序的代码去处理同一个资源的情况,把线程和程序的代码、数据有效分离,较好的体现

      了面向对象的设计思想


线程同步
  • 同步代码块解决数据安全问题

    • 安全问题出现的条件

      • 是多线程环境
      • 有共享数据
      • 有多条语句操作共享数据
    • 如何解决多线程安全问题呢?

      • 基本思想:让程序没有安全问题的环境 怎么实现呢?
      • 把多条语句操作共享数据的代码给锁起来,让任意时刻只能有一个线程执行即可
      • Java提供了同步代码块的方式来解决
    • 同步代码块格式:

      synchronized(任意对象) { 
        多条语句操作共享数据的代码
      }
      

      synchronized(任意对象):就相当于给代码加锁了,任意对象就可以看成是一把锁

    • 同步的好处和弊端

      • 好处:解决了多线程的数据安全问题
      • 弊端:当线程很多时,因为每个线程都会去判断同步上的锁,这是很耗费资源的,无形中会降低程序的运行效率
    • 代码实例:

      synchronized (obj) {
        //t1进来后,就会把这段代码给锁起来 
        if (tickets > 0) { 
          try {
            Thread.sleep(100); 
               //t1休息100毫秒 
          } catch (InterruptedException e) {
            e.printStackTrace(); 
          }//窗口1正在出售第100张票
      		System.out.println(Thread.currentThread().getName() + "正在出售 第" + tickets + "张票"); 
          	tickets--; //tickets = 99;
          
        }
        //t1出来了,这段代码的锁就被释放了
      
  • 同步方法解决数据安全问题

    • 同步方法的格式

      同步方法就是将synchronized关键字加到方法上

      修饰符 synchronized 返回值类型 方法名(方法参数)
      { 
        方法体;
      }
      

      同步方法的锁对象是this

    • 静态同步方法

      同步静态方法就是将synchronized关键字加到静态方法上

      修饰符 static synchronized 返回值类型 方法名(方法参数) { 
        方法体; 
      }
      

      静态同步方法的锁对象是.class


  • Lock锁

    Lock是接口不能直接实例化,这里采用它的实现类ReentrantLock来实例化

    • ReentrantLock构造方法

      方法名说明
      ReentrantLock()创建一个ReentrantLock的实例
    • Lock锁的加锁解锁方法:

      方法名说明
      void lock()获得锁
      void unlock()释放锁

      代码实例

      private Lock lock = new ReentrantLock();
        //t1进来后,就会把这段代码给锁起来 
      try{
        lock.lock()
        if (tickets > 0) { 
          try {
            Thread.sleep(100); 
               //t1休息100毫秒 
          } catch (InterruptedException e) {
            e.printStackTrace(); 
          }//窗口1正在出售第100张票
      		System.out.println(Thread.currentThread().getName() + "正在出售 第" + tickets + "张票"); 
          	tickets--; //tickets = 99;
        }
        finally { 
          lock.unlock();
        }
          
        
        
      

生产者和消费者

生产者消费者模式是一个十分经典的多线程协作的模式,弄懂生产者消费者问题能够让我们对多线程编程的

理解更加深刻。

所谓生产者消费者问题,实际上主要是包含了两类线程:

一类是生产者线程用于生产数据

一类是消费者线程用于消费数据

为了解耦生产者和消费者的关系,通常会采用共享的数据区域,就像是一个仓库

生产者生产数据之后直接放置在共享数据区中,并不需要关心消费者的行为

消费者只需要从共享数据区中去获取数据,并不需要关心生产者的行为

在这里插入图片描述

  • Object类的等待和唤醒方法

    方法名说明
    void wait()导致当前线程等待,直到另一个线程调用该对象的 notify()方法或 notifyAll()方法
    void notify()唤醒正在等待对象监视器的单个线程
    void notifyAll()唤醒正在等待对象监视器的所有线程

    代码实例

    案例需求

    生产者消费者案例中包含的类:

    奶箱类(Box):定义一个成员变量,表示第x瓶奶,提供存储牛奶和获取牛奶的操作

    生产者类(Producer):实现Runnable接口,重写run()方法,调用存储牛奶的操作

    消费者类(Customer):实现Runnable接口,重写run()方法,调用获取牛奶的操作

    测试类(BoxDemo):里面有main方法,main方法中的代码步骤如下

    ①创建奶箱对象,这是共享数据区域

    ②创建消费者创建生产者对象,把奶箱对象作为构造方法参数传递,因为在这个类中要调用存储牛奶的操作

    ③对象,把奶箱对象作为构造方法参数传递,因为在这个类中要调用获取牛奶的操作

    ④创建2个线程对象,分别把生产者对象和消费者对象作为构造方法参数传递

    ⑤启动线程

    public class Box {
        //定义一个成员变量,表示第x瓶奶
        private int milk;
        //定义一个成员变量,表示奶箱的状态
        private boolean state = false;
    
        //提供存储牛奶和获取牛奶的操作
        public synchronized void put(int milk) {
            //如果有牛奶,等待消费
            if(state) {
                try {
                    wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
    
            //如果没有牛奶,就生产牛奶
            this.milk = milk;
            System.out.println("送奶工将第" + this.milk + "瓶奶放入奶箱");
    
            //生产完毕之后,修改奶箱状态
            state = true;
    
            //唤醒其他等待的线程
            notifyAll();
        }
    
        public synchronized void get() {
            //如果没有牛奶,等待生产
            if(!state) {
                try {
                    wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
    
            //如果有牛奶,就消费牛奶
            System.out.println("用户拿到第" + this.milk + "瓶奶");
    
            //消费完毕之后,修改奶箱状态
            state = false;
    
            //唤醒其他等待的线程
            notifyAll();
        }
    }
    
    public class Producer implements Runnable {
        private Box b;
    
        public Producer(Box b) {
            this.b = b;
        }
    
        @Override
        public void run() {
            for(int i=1; i<=30; i++) {
                b.put(i);
            }
        }
    }
    
    
    public class Customer implements Runnable {
        private Box b;
    
        public Customer(Box b) {
            this.b = b;
        }
    
        @Override
        public void run() {
            while (true) {
                b.get();
            }
        }
    }
    
    /*
        生产者消费者案例中包含的类:
            1:奶箱类(Box):定义一个成员变量,表示第x瓶奶,提供存储牛奶和获取牛奶的操作
            2:生产者类(Producer):实现Runnable接口,重写run()方法,调用存储牛奶的操作
            3:消费者类(Customer):实现Runnable接口,重写run()方法,调用获取牛奶的操作
            4:测试类(BoxDemo):里面有main方法,main方法中的代码步骤如下
                A:创建奶箱对象,这是共享数据区域
                B:创建生产者对象,把奶箱对象作为构造方法参数传递,因为在这个类中要调用存储牛奶的操作
                C:创建消费者对象,把奶箱对象作为构造方法参数传递,因为在这个类中要调用获取牛奶的操作
                D:创建2个线程对象,分别把生产者对象和消费者对象作为构造方法参数传递
                E:启动线程
     */
    public class BoxDemo {
        public static void main(String[] args) {
            //创建奶箱对象,这是共享数据区域
            Box b = new Box();
    
            //创建生产者对象,把奶箱对象作为构造方法参数传递,因为在这个类中要调用存储牛奶的操作
            Producer p = new Producer(b);
            //创建消费者对象,把奶箱对象作为构造方法参数传递,因为在这个类中要调用获取牛奶的操作
            Customer c = new Customer(b);
    
            //创建2个线程对象,分别把生产者对象和消费者对象作为构造方法参数传递
            Thread t1 = new Thread(p);
            Thread t2 = new Thread(c);
    
            //启动线程
            t1.start();
            t2.start();
        }
    }
    

网络编程

  • 概述

    • 计算机网络

      是指将地理位置不同的具有独立功能的多台计算机及其外部设备,通过通信线路连接起来,在网络操作系

      统,网络管理软件及网络通信协议的管理和协调下,实现资源共享和信息传递的计算机系统

    • 网络编程

      在网络通信协议下,实现网络互连的不同计算机上运行的程序间可以进行数据交换

  • 网络编程的三要素

    • IP地址

      要想让网络中的计算机能够互相通信,必须为每台计算机指定一个标识号,通过这个标识号来指定要接收数据的计算机和识别发送的计算机,而IP地址就是这个标识号。也就是设备的标识

    • 端口

      网络的通信,本质上是两个应用程序的通信。每台计算机都有很多的应用程序,那么在网络通信时,如何区分这些应用程序呢?如果说IP地址可以唯一标识网络中的设备,那么端口号就可以唯一标识设备中的应用程序了。也就是应用程序的标识

    • 协议

      通过计算机网络可以使多台计算机实现连接,位于同一个网络中的计算机在进行连接和通信时需要遵守一定的规则,这就好比在道路中行驶的汽车一定要遵守交通规则一样。在计算机网络中,这些连接和通信的规则被称为网络通信协议,它对数据的传输格式、传输速率、传输步骤等做了统一规定,通信双方必须同时遵守才能完成数据交换。常见的协议有UDP协议和TCP协议

  • InetAddress类

    此类表示Internet协议(IP)地址

    • 相关方法

      方法名说明
      static InetAddress getByName(String host)确定主机名称的IP地址。主机名称可以是机器名称,也可以 是IP地址
      String getHostName()获取此IP地址的主机名
      String getHostAddress()返回文本显示中的IP地址字符串
  • 端口和*

    • 端口

      设备上应用程序的唯一标识

    • 端口号

      用两个字节表示的整数,它的取值范围是065535。其中,01023之间的端口号用于一些知名的网络服

      务和应用,普通的应用程序需要使用1024以上的端口号。如果端口号被另外一个服务或应用所占用,会

      导致当前程序启动失败

    • 协议

      计算机网络中,连接和通信的规则被称为网络通信协议

    • UDP协议

      用户数据报协议(User Datagram Protocol)

      UDP是无连接通信协议,即在数据传输时,数据的发送端和接收端不建立逻辑连接。简单来说,当一台

      计算机向另外一台计算机发送数据时,发送端不会确认接收端是否存在,就会发出数据,同样接收端在

      收到数据时,也不会向发送端反馈是否收到数据。

      由于使用UDP协议消耗资源小,通信效率高,所以通常都会用于音频、视频和普通数据的传输端口

      设备上应用程序的唯一标识

      例如视频会议通常采用UDP协议,因为这种情况即使偶尔丢失一两个数据包,也不会对接收结果产生太

      大影响。但是在使用UDP协议传送数据时,由于UDP的面向无连接性,不能保证数据的完整性,因此在

      传输重要数据时不建议使用UDP协议

    • TCP协议

      传输控制协议 (Transmission Control Protocol)

      TCP协议是面向连接的通信协议,即传输数据之前,在发送端和接收端建立逻辑连接,然后再传输数

      据,它提供了两台计算机之间可靠无差错的数据传输。在TCP连接中必须要明确客户端与服务器端,由

      客户端向服务端发出连接请求,每次连接的创建都需要经过“三次握手”

      三次握手:TCP协议中,在发送数据的准备阶段,客户端与服务器之间的三次交互,以保证连接的可靠

      第一次握手,客户端向服务器端发出连接请求,等待服务器确认

      第二次握手,服务器端向客户端回送一个响应,通知客户端收到了连接请求

      第三次握手,客户端再次向服务器端发送确认信息,确认连接

      完成三次握手,连接建立后,客户端和服务器就可以开始进行数据传输了。由于这种面向连接的特性,

      TCP协议可以保证传输数据的安全,所以应用十分广泛。例如上传文件、下载文件、浏览网页等


UDP通信程序
  • UDP发送数据

  • Java中的UDP通信

    UDP协议是一种不可靠的网络协议,它在通信的两端各建立一个Socket对象,但是这两个Socket只是发

    送,接收数据的对象,因此对于基于UDP协议的通信双方而言,没有所谓的客户端和服务器的概念

    Java提供了DatagramSocket类作为基于UDP协议的Socket

  • 构造方法

    方法名说明
    DatagramSocket()创建数据报套接字并将其绑定到本机地址上的任何可用数据
    DatagramPacket(byte[] buf,int len,InetAddress add,int port)创建数据包,发送长度为len的数据包到指定主机的指定端口
  • 相关方法

    方法名说明
    void send(DatagramPacket p)发送数据报包
    void close()关闭数据报套接字
    void receive(DatagramPacket p)从此套接字接受数据报包
  • 发送数据步骤

    1. 创建发送端的Socket对象(DatagramSocket)
    2. 创建数据,并把数据打包
    3. 调用DatagramSocket对象的方法发送数据
    4. 关闭发送端

代码实例:

/*
    UDP发送数据的步骤
        1:创建发送端的Socket对象(DatagramSocket)
        2:创建数据,并把数据打包
        3:调用DatagramSocket对象的方法发送数据
        4:关闭发送端
 */
public class SendDemo {
    public static void main(String[] args) throws IOException {
        //创建发送端的Socket对象(DatagramSocket)
        // DatagramSocket() 构造数据报套接字并将其绑定到本地主机上的任何可用端口
        DatagramSocket ds = new DatagramSocket();

        //创建数据,并把数据打包
        //DatagramPacket(byte[] buf, int length, InetAddress address, int port)
        //构造一个数据包,发送长度为 length的数据包到指定主机上的指定端口号。
        byte[] bys = "hello,udp,我来了".getBytes();
//        int length = bys.length;
//        InetAddress address = InetAddress.getByName("192.168.1.66");
//        int port = 10086;
//        DatagramPacket dp = new DatagramPacket(bys,length,address,port);
        DatagramPacket dp = new DatagramPacket(bys,bys.length,InetAddress.getByName("192.168.1.66"),10086);

        //调用DatagramSocket对象的方法发送数据
        //void send(DatagramPacket p) 从此套接字发送数据报包
        ds.send(dp);

        //关闭发送端
        //void close() 关闭此数据报套接字
        ds.close();
    }
}

  • UDP接收数据

  • 接收数据的步骤

    1. 创建接收端的Socket对象(DatagramSocket)
    2. 创建一个数据包,用于接收数据
    3. 调用DatagramSocket对象的方法接收数据
    4. 解 析数据包,并把数据在控制台显示
    5. 关闭接收端
  • 构造方法

    方法名说明
    DatagramPacket(byte[] buf, int len)创建一个DatagramPacket用于接收长度为len的数据包
  • 相关方法

    方法名说明
    byte[] getData()返回数据缓冲区
    int getLength()返回要发送的数据的长度或接收的数据的长度

代码演示:

/*
    UDP接收数据的步骤
        1:创建接收端的Socket对象(DatagramSocket)
        2:创建一个数据包,用于接收数据
        3:调用DatagramSocket对象的方法接收数据
        4:解析数据包,并把数据在控制台显示
        5:关闭接收端
 */
public class ReceiveDemo {
    public static void main(String[] args) throws IOException {
        //创建接收端的Socket对象(DatagramSocket)
        //DatagramSocket(int port) 构造数据报套接字并将其绑定到本地主机上的指定端口
        DatagramSocket ds = new DatagramSocket(10086);

        //创建一个数据包,用于接收数据
        //DatagramPacket(byte[] buf, int length) 构造一个 DatagramPacket用于接收长度为 length数据包
        byte[] bys = new byte[1024];
        DatagramPacket dp = new DatagramPacket(bys,bys.length);

        //调用DatagramSocket对象的方法接收数据
        ds.receive(dp);

        //解析数据包,并把数据在控制台显示
        //byte[] getData() 返回数据缓冲区
//        byte[] datas = dp.getData();
        //int getLength() 返回要发送的数据的长度或接收到的数据的长度
//        int len = dp.getLength();
//        String dataString = new String(datas,0,len);
//        System.out.println("数据是:" + dataString);
        System.out.println("数据是:" + new String(dp.getData(),0,dp.getLength()));

        //关闭接收端
        ds.close();
    }
}
TCP通讯程序
  • TCP发送数据

  • Java中的TCP通信

    Java对基于TCP协议的的网络提供了良好的封装,使用Socket对象来代表两端的通信端口,并通过

    Socket产生IO流来进行网络通信。

    Java为客户端提供了Socket类,为服务器端提供了ServerSocket类

  • 构造方法:

    方法名说明
    Socket(InetAddress address,int port)创建流套接字并将其连接到指定IP指定端口号
    Socket(String host, int port)创建流套接字并将其连接到指定主机上的指定端口号
  • 相关方法

    方法名说明
    InputStream getInputStream()返回此套接字的输入流
    OutputStream getOutputStream()返回此套接字的输出流
  • 代码演示:

    /*
        TCP发送数据的步骤
            1:创建客户端的Socket对象(Socket)
            2:获取输出流,写数据
            3:释放资源
     */
    public class ClientDemo {
        public static void main(String[] args) throws IOException {
            //创建客户端的Socket对象(Socket)
            //Socket(InetAddress address, int port) 创建流套接字并将其连接到指定IP地址的指定端口号
    //        Socket s = new Socket(InetAddress.getByName("192.168.1.66"),10000);
            //Socket(String host, int port) 创建流套接字并将其连接到指定主机上的指定端口号
            Socket s = new Socket("192.168.1.66",10000);
    
            //获取输出流,写数据
            //OutputStream getOutputStream() 返回此套接字的输出流
            OutputStream os = s.getOutputStream();
            os.write("hello,tcp,我来了".getBytes());
    
            //释放资源
            s.close();
        }
    }
    

  • TCP接收数据

  • 构造方法:

    方法名说明
    ServletSocket(int port)创建绑定到指定端口的服务器套接字
  • 相关方法

    方法名说明
    Socket accept()监听要连接到此的套接字并接受它
  • 代码演示

    /*
        TCP接收数据的步骤
            1:创建服务器端的Socket对象(ServerSocket)
            2:获取输入流,读数据,并把数据显示在控制台
            3:释放资源
     */
    public class ServerDemo {
        public static void main(String[] args) throws IOException {
            //创建服务器端的Socket对象(ServerSocket)
            //ServerSocket(int port) 创建绑定到指定端口的服务器套接字
            ServerSocket ss = new ServerSocket(10000);
    
            //Socket accept() 侦听要连接到此套接字并接受它
            Socket s = ss.accept();
    
            //获取输入流,读数据,并把数据显示在控制台
            InputStream is = s.getInputStream();
            byte[] bys = new byte[1024];
            int len = is.read(bys);
            String data = new String(bys,0,len);
            System.out.println("数据是:" + data);
    
            //释放资源
            s.close();
            ss.close();
        }
    }
    
    

Lambda和方法应用

  • 体验Lambda表达式案例需求

    • 启动一个线程,在控制台输出一句话:多线程程序启动了

      实现方式一

      实现步骤

    1. 定义一个类MyRunnable实现Runnable接口,重写run()方法

    2. 创建MyRunnable类的对象

    3. 创建Thread类的对象,把MyRunnable的对象作为构造参数传递

    4. 启动线程

      实现方式二

      匿名内部类的方式改进

      实现方式三

      Lambda表达式的方式改进

    代码实例:

    /*
        需求:启动一个线程,在控制台输出一句话:多线程程序启动了
     */
    public class LambdaDemo {
        public static void main(String[] args) {
            //实现类的方式实现需求
    //        MyRunnable my = new MyRunnable();
    //        Thread t = new Thread(my);
    //        t.start();
    
            //匿名内部类的方式改进
    //        new Thread(new Runnable() {
    //            @Override
    //            public void run() {
    //                System.out.println("多线程程序启动了");
    //            }
    //        }).start();
    
            //Lambda表达式的方式改进
            new Thread( () -> {
                System.out.println("多线程程序启动了");
            } ).start();
        }
    }
    
    • 函数式编程思想

      函数式思想则尽量忽略面向对象的复杂语法:“强调做什么,而不是以什么形式去做”

      而我们要学习的Lambda表达式就是函数式思想的体现

  • Lambda表达式的标准格式

    格式:

    • (形式参数) -> {代码块}
    • 形式参数:如果有多个参数,参数之间用逗号隔开;如果没有参数,留空即可
    • ->:由英文中画线和大于符号组成,固定写法。代表指向动作
    • 代码块:是我们具体要做的事情,也就是以前我们写的方法体内容

    组成Lambda表达式的三要素:

    • 形式参数,箭头,代码块
  • Lambda表达式的三种例子

    • 无参无返回值

      //接口 
      public interface Eatable { 
        void eat(); 
      }
      //实现类
      public class EatableImpl implements Eatable {
        @Override 
        public void eat() { 
          System.out.println("一天一苹果,医生远离我");
        } 
      }
      
      //测试类
      public class EatableDemo {
        public static void main(String[] args) {
          //Lambda表达式
          useEatable(() -> {
            System.out.println("一天一苹果,医生远离我");
          }
              ); 
        }
        private static void useEatable(Eatable e) { 
          e.eat(); 
        } 
      }
      

    • 有参无返回值:

      //接口
      public interface Flyable {
        void fly(String s);
      }
      //Lambda
      useFlyable((String s) -> {
        System.out.println(s);
        System.out.println("飞机自驾游");
      });
      private static void useFlyable(Flyable f) {
        f.fly("风和日丽,晴空万里"); 
      }
      
      
    • 有参有返回值

      //接口
      public interface Addable { 
        int add(int x,int y); 
      }
      
      public class AddableDemo { 
        public static void main(String[] args) {
          //在主方法中调用useAddable方法
          useAddable((int x,int y) -> {
            return x + y; 
          });
        }
      private static void useAddable(Addable a) {
        int sum = a.add(10, 20);
        System.out.println(sum); 
      }
      }
      
  • Lambda的省略模式

    省略的规则

    • 参数类型可以省略。但是有多个参数的情况下,不能只省略一个
    • 如果参数有且仅有一个,那么小括号可以省略
    • 如果代码块的语句只有一条,可以省略大括号和分号,和return关键字
  • Lambda表达式的注意事项

    • 使用Lambda必须要有接口,并且要求接口中有且仅有一个抽象方法

    • 必须有上下文环境,才能推导出Lambda对应的接口

      • 根据局部变量的赋值得知Lambda对应的接口

        Runnable r = () -> System.out.println(“Lambda表达式”);

      • 根据调用方法的参数得知Lambda对应的接口
        new Thread(() -> System.out.println(“Lambda表达式”)).start();


方法引用

在使用Lambda表达式的时候,我们实际上传递进去的代码就是一种解决方案:拿参数做操作、

如果我们在Lambda中所指定的操作方案,已经有地方存在相同方案,那是否还有必要再写重复逻辑呢?答案肯定。没有必要

  • 代码实例:

    public interface Printable {
      void printString(String s); 
    }
    public class PrintableDemo {
      public static void main(String[] args) {
        //在主方法中调用usePrintable方法
        // usePrintable((String s) -> {
        // System.out.println(s); 
        // });
        //Lambda简化写法 
        usePrintable(s -> System.out.println(s)); 
        //方法引用
        usePrintable(System.out::println);
      }
      private static void usePrintable(Printable p) {
        p.printString("爱生活爱Java"); 
      }
    }
    
  • 方法引用符

    • 方法引用符

      :: 该符号为引用运算符,而它所在的表达式被称为方法引用

    • 推导与省略

      • 如果使用Lambda,那么根据“可推导就是可省略”的原则,无需指定参数类型,也无需指定的重载形式,它们都将被自动推导
      • 如果使用方法引用,也是同样可以根据上下文进行推导
      • 方法引用是Lambda的孪生兄弟
  • 引用类方法

    引用类方法,其实就是引用类的静态方法

    • 格式

      类名::静态方法

    • 范例

      Integer::parseInt

      Integer类的方法:public static int parseInt(String s) 将此String转换为int类型数据

    • 代码实例:

      public interface Converter { 
        int convert(String s);
      }
      public class ConverterDemo { 
        public static void main(String[] args) { 
          //Lambda写法
          useConverter(s -> Integer.parseInt(s));
          //引用类方法
          useConverter(Integer::parseInt); 
        }
        private static void useConverter(Converter c) {
          int number = c.convert("666"); 
          System.out.println(number); 
        } 
      }
      
    • 使用说明
      Lambda表达式被类方法替代的时候,它的形式参数全部传递给静态方法作为参数

  • 引用对象的实例方法

    引用对象的实例方法,其实就引用类中的成员方法

    • 格式

      对象::成员方法

    • 范例

      “HelloWorld”::toUpperCase

      String类中的方法:public String toUpperCase() 将此String所有字符转换为大写

    • 代码演示:

      public class PrintString {
        //把字符串参数变成大写的数据,然后在控制台输出
        public void printUpper(String s) { 
          String result = s.toUpperCase(); 
          System.out.println(result);
        }
      }
      public interface Printer { 
        void printUpperCase(String s);
      }
      public class PrinterDemo {
        public static void main(String[] args) { 
          //Lambda简化写法
          usePrinter(s -> System.out.println(s.toUpperCase()));
          //引用对象的实例方法
          PrintString ps = new PrintString();
          usePrinter(ps::printUpper); }
        private static void usePrinter(Printer p) {
          p.printUpperCase("HelloWorld");
        }
      }
      
  • 引用类的实例方法

    引用类的实例方法,其实就是引用类中的成员方法

    • 格式

      类名::成员方法

    • 范例

      String::substring

      public String substring(int beginIndex,int endIndex)

      从beginIndex开始到endIndex结束,截取字符串。返回一个子串,子串的长度为endIndex-beginIndex

    • 代码演示:

      public interface MyString {
        String mySubString(String s,int x,int y);
      }
      public class MyStringDemo {
        public static void main(String[] args) {
          //Lambda简化写法 
          useMyString((s,x,y) -> s.substring(x,y));
          //引用类的实例方法 
          useMyString(String::substring); 
        }
        private static void useMyString(MyString my) { 
          String s = my.mySubString("HelloWorld", 2, 5);
          System.out.println(s);
        } 
      }
      
    • 使用说明

      Lambda表达式被类的实例方法替代的时候 第一个参数作为调用者 后面的参数全部传递给该方法作为参数

  • 引用构造器

    引用构造器,其实就是引用构造方法

    • 格式

      类名::new

    • 范例

      Student::new

    • 代码演示

      public interface StudentBuilder {
        Student build(String name,int age); 
      }
      public class StudentDemo {
        public static void main(String[] args) {
          //Lambda简化写法
          useStudentBuilder((name,age) -> new Student(name,age));
          //引用构造器 
          useStudentBuilder(Student::new); 
        }
        private static void useStudentBuilder(StudentBuilder sb) {
          Student s = sb.build("林青霞", 30); 
          System.out.println(s.getName() + "," + s.getAge()); 
        }
      }
      
    • 使用说明
      Lambda表达式被构造器替代的时候,它的形式参数全部传递给构造器作为参数


函数式接口

概念

  • 有且仅有一个抽象方法的接口

    @FunctionalInterface检测一个接口是不是函数式接口

    放在接口定义的上方:如果接口是函数式接口,编译通过;如果不是,编译失败

  • 注意事项

    我们自己定义函数式接口的时候,@FunctionalInterface是可选的,就算我不写这个注解,只要保证满足函数

    式接口定义的条件,也照样是函数式接口。但是,建议加上该注解


Strem流

Strem流代码演示

按照下面的要求完成集合的创建和遍历

​ 创建一个集合,存储多个字符串元素

​ 把集合中所有以"张"开头的元素存储到一个新的集合

​ 把"张"开头的集合中的长度为3的元素存储到一个新的集合

​ 遍历上一步得到的集合

  • 使用Strem流来演示:

    public class StreamDemo {
      public static void main(String[] args) {
        //创建一个集合,存储多个字符串元素
        ArrayList<String> list = new ArrayList<String>(); 
        list.add("林青霞");
        list.add("张曼玉");
        list.add("王祖贤"); 
        list.add("柳岩"); 
        list.add("张敏"); 
        list.add("张无忌"); 
        //Stream流来改进
        list.stream().filter(s -> s.startsWith("张")).filter(s -> s.length() == 3).forEach(System.out::println); 
      }
    }
    
  • Strem流的好处

    • 直接阅读代码的字面意思即可完美展示无关逻辑方式的语义:获取流、过滤姓张、过滤长度为3、逐一打

    • Stream流把真正的函数式编程风格引入到Java中

Strem流常见的生成方式

  • Strem流的思想

    在这里插入图片描述

  • 生成Strem流的方式

    • Collection体系集合

      使用默认方法stream()生成流, default Stream stream()

    • Map体系集合

      把Map转成Set集合,间接的生成流

    • 数组

      通过Stream接口的静态方法of(T… values)生成流

  • 代码演示

    public class StreamDemo { public static void main(String[] args) {
      //Collection体系的集合可以使用默认方法stream()生成流
      List<String> list = new ArrayList<String>();
      Stream<String> listStream = list.stream();
      Set<String> set = new HashSet<String>(); 
      Stream<String> setStream = set.stream(); 
      //Map体系的集合间接的生成流 
      Map<String,Integer> map = new HashMap<String, Integer>();
      Stream<String> keyStream = map.keySet().stream();
      Stream<Integer> valueStream = map.values().stream(); 
      Stream<Map.Entry<String, Integer>> entryStream = map.entrySet().stream();
      //数组可以通过Stream接口的静态方法of(T... values)生成流
      String[] strArray = {"hello","world","java"};
      Stream<String> strArrayStream = Stream.of(strArray);
      Stream<String> strArrayStream2 = Stream.of("hello", "world", "java"); 
      Stream<Integer> intStream = Stream.of(10, 20, 30); 
    }
                            }
    
  • Strem流的中间操作方式

    概念:

    中间操作的意思是,执行完此方法之后,Stream流依然可以继续执行其他操作。

    常见方法

    方法名说明
    Stream fifilter(Predicate predicate)用于对流中的数据进行过滤
    Stream limit(long maxSize)返回此流中的元素组成的流,截取前指定参数个数的数据
    Stream skip(long n)跳过指定参数个数的数据,返回由该流的剩余元素组成的流
    static Stream concat(Stream a, Stream b)合并a和b两个流为一个流
    Stream distinct()返回由该流的不同元素(根据Object.equals(Object) )组成的流
    Stream sorted()返回由此流的元素组成的流,根据自然顺序排序
    Stream sorted(Comparator comparator)返回由该流的元素组成的流,根据提供的Comparator进行
    Stream map(Function mapper)返回由给定函数应用于此流的元素的结果组成的流
    IntStream mapToInt(ToIntFunction mapper)返回一个IntStream其中包含将给定函数应用于此流的元素的结果

Strem流终结操作方法

  • 概念:

    • 终结操作的意思是,执行完此方法之后,Stream流将不能再执行其他操作。
  • 常见方法

    方法名说明
    void forEach(Consumer action)对此流的每个元素执行操作
    long count()返回此流中的元素数

Strem流的收集方法

  • 概念:

    • 对数据使用Stream流的方式操作完毕后,可以把流中的数据收集到集合中。
  • 常见方法

    方法名说明
    R collect(Collector collector)把结果收集到集合中
  • 工具类Collectors提供了具体的收集方式

    方法名说明
    public static Collector toList()把元素收集到List集合中
    public static Collector toSet()把元素收集到Set集合中
    public static Collector toMap(Function keyMapper,Function valueMapper)把元素收集到Map集合 中

类加载器

概述

​ 当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过类的加载,类的连接,类的初始

化这三个步骤来对类进行初始化。如果不出现意外情况,JVM将会连续完成这三个步骤,所以有时也把

这三个步骤统称为类加载或者类初始化

类的加载

​ 就是指将class文件读入内存,并为之创建一个 java.lang.Class 对象

​ 任何类被使用时,系统都会为之建立一个 java.lang.Class 对象

类的链接

​ 验证阶段:用于检验被加载的类是否有正确的内部结构,并和其他类协调一致

​ 准备阶段:负责为类的类变量分配内存,并设置默认初始化值

​ 解析阶段:将类的二进制数据中的符号引用替换为直接引用

类的初始化

​ 在该阶段,主要就是对类变量进行初始化

类的初始化步骤

​ 假如类还未被加载和连接,则程序先加载并连接该类

​ 假如该类的直接父类还未被初始化,则先初始化其直接父类

​ 假如类中有初始化语句,则系统依次执行这些初始化语句

​ 注意:在执行第2个步骤的时候,系统对直接父类的初始化步骤也遵循初始化步骤1-3

类的初始化时机

​ 创建类的实例

​ 调用类的类方法

​ 访问类或者接口的类变量,或者为该类变量赋值

​ 使用反射方式来强制创建某个类或接口对应的java.lang.Class对象

​ 初始化某个类的子类

​ 直接使用java.exe命令来运行某个主类

类加载器的作用

​ 负责将.class文件加载到内存中,并为之生成对应的 java.lang.Class 对象。虽然我们不用过分关心类加载机

制,但是了解这个机制我们就能更好的理解程序的运行!

JVM的类加载机制

  • 全盘负责:就是当一个类加载器负责加载某个Class时,该Class所依赖的和引用的其他Class也将由该类加载

    器负责载入,除非显示使用另外一个类加载器来载入

  • 父类委托:就是当一个类加载器负责加载某个Class时,先让父类加载器试图加载该Class,只有在父类加载器

    无法加载该类时才尝试从自己的类路径中加载该类

  • 缓存机制:保证所有加载过的Class都会被缓存,当程序需要使用某个Class对象时,类加载器先从缓存区中搜

    索该Class,只有当缓存区中不存在该Class对象时,系统才会读取该类对应的二进制数据,并将其转换成Class对象,存储到缓存区

Java中的内置类加载器

Bootstrap class loader:它是虚拟机的内置类加载器,通常表示为null ,并且没有父null

Platform class loader:平台类加载器可以看到所有平台类 ,平台类包括由平台类加载器或其祖先定义的Java

SE平台API,其实现类和JDK特定的运行时类

System class loader:它也被称为应用程序类加载器 ,与平台类加载器不同。 系统类加载器通常用于定义应

用程序类路径,模块路径和JDK特定工具上的类

类加载器的继承关系:System的父加载器为Platform,而Platform的父加载器为Bootstrap

ClassLoader 中的两个方法

方法名说明
static ClassLoader getSystemClassLoader()返回用于委派的系统类加载器
ClassLoader getParent()返回父类加载器进行委派
  • 代码演示

    public class ClassLoaderDemo { 
      public static void main(String[] args) { 
        //static ClassLoader getSystemClassLoader():返回用于委派的系统类加载器
        ClassLoader c = ClassLoader.getSystemClassLoader();
        System.out.println(c); //AppClassLoader
        //ClassLoader getParent():返回父类加载器进行委派
        ClassLoader c2 = c.getParent(); 
        System.out.println(c2); //PlatformClassLoader
        ClassLoader c3 = c2.getParent(); System.out.println(c3); //null
      }
    }
    

反射

反射的概述

是指在运行时去获取一个类的变量和方法信息。然后通过获取到的信息来创建对象,调用方法的一种机制。

由于这种动态性,可以极大的增强程序的灵活性,程序不用在编译期就完成确定,在运行期仍然可以扩展

获取反射的三种方式

  • 类名.class属性
  • 对象名.getClass()方法
  • Class.forName(全类名)方法

代码实例:

public class ReflectDemo {
  public static void main(String[] args) throws ClassNotFoundException { 
    //使用类的class属性来获取该类对应的Class对象
    Class<Student> c1 = Student.class; 
    System.out.println(c1);
    Class<Student> c2 = Student.class; 
    System.out.println(c1 == c2); 
    System.out.println("--------"); 
    //调用对象的getClass()方法,返回该对象所属类对应的Class对象
    Student s = new Student();
    Class<? extends Student> c3 = s.getClass(); 
    System.out.println(c1 == c3); 
    System.out.println("--------"); 
    //使用Class类中的静态方法forName(String className)
    Class<?> c4 = Class.forName("com.itheima_02.Student");
    System.out.println(c1 == c4); 
  }
}

放射获取构造方法

  • Class类获取构造方法对象的方法

    方法名说明
    Constructor<?>[] getConstructors()返回所有公共构造方法对象的数组
    Constructor<?>[] getDeclaredConstructors()返回所有构造方法对象的数组
    Constructor getConstructor(Class<?>… parameterTypes)返回单个公共构造方法对象
    Constructor getDeclaredConstructor(Class<?>… parameterTypes)返回单个构造方法对象
  • Constructor类用于创建对象的方法

    方法名说明
    T newInstance(Object…initargs)根据指定的构造方法创建对象
  • 代码演示
    通过反射获取公共的构造方法并创建对象

    package com.itheima_02;
    
    public class Student {
        //成员变量:一个私有,一个默认,一个公共
        private String name;
        int age;
        public String address;
    
        //构造方法:一个私有,一个默认,两个公共
        public Student() {
        }
    
        private Student(String name) {
            this.name = name;
        }
    
        Student(String name, int age) {
            this.name = name;
            this.age = age;
        }
    
        public Student(String name, int age, String address) {
            this.name = name;
            this.age = age;
            this.address = address;
        }
    
        //成员方法:一个私有,四个公共
        private void function() {
            System.out.println("function");
        }
    
        public void method1() {
            System.out.println("method");
        }
    
        public void method2(String s) {
            System.out.println("method:" + s);
        }
    
        public String method3(String s, int i) {
            return s + "," + i;
        }
    
        @Override
        public String toString() {
            return "Student{" +
                    "name='" + name + '\'' +
                    ", age=" + age +
                    ", address='" + address + '\'' +
                    '}';
        }
    }
    
    
    package com.itheima_02;
    
    /*
        三种方式获取Class对象
            1:使用类的class属性来获取该类对应的Class对象。举例:Student.class将会返回Student类对应的Class对象
            2:调用对象的getClass()方法,返回该对象所属类对应的Class对象
                该方法是Object类中的方法,所有的Java对象都可以调用该方法
            3:使用Class类中的静态方法forName(String className),该方法需要传入字符串参数,该字符串参数的值是某个类的全路径,也就是完整包名的路径
     */
    public class ReflectDemo {
        public static void main(String[] args) throws ClassNotFoundException {
            //使用类的class属性来获取该类对应的Class对象
            Class<Student> c1 = Student.class;
            System.out.println(c1);
    
            Class<Student> c2 = Student.class;
            System.out.println(c1 == c2);
            System.out.println("--------");
    
            //调用对象的getClass()方法,返回该对象所属类对应的Class对象
            Student s = new Student();
            Class<? extends Student> c3 = s.getClass();
            System.out.println(c1 == c3);
            System.out.println("--------");
    
            //使用Class类中的静态方法forName(String className)
            Class<?> c4 = Class.forName("com.itheima_02.Student");
            System.out.println(c1 == c4);
        }
    }
    
    

反射获取成员变量

方法名说明
Field[] getFields()返回所有公共成员变量对象的数组
Field[] getDeclaredFields()返回所有成员变量对象的数组
Field getField(String name)返回单个公共成员变量对象
Field getDeclaredField(String name)返回单个成员变量对象
  • Field类用于给成员变量赋值的方法

    方法名说明
    voidset(Object obj,Object value)给obj对象的成员变量赋值为value

反射获取成员方法

  • Class类获取成员方法对象的方法

    方法名说明
    Method[] getMethods()返回所有公共成员方法对象的数组,包括继承的
    Method[] getDeclaredMethods()返回所有成员方法对象的数组,不包括 继承的
    Method getMethod(String name, Class<?>… parameterTypes)返回单个公共成员方法对象
    Method getDeclaredMethod(String name, Class<?>… parameterTypes)返回单个成员方法对象
  • Method类用于执行方法的方法

    方法名说明
    Object invoke(Object obj,Object… args)调用obj对象的成员方法,参数是args,返回值是Object类型

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值