java随堂小记

  • JavaSE进阶
  • 数据库
  • 前端技术
  • web开发
  • 框架技术
  • 项目1
  • 微服务技术
  • 项目2
  • 项目3

面向对象三大特征:

  1. 封装
  2. 继承
  3. 多态

学习技巧:

问题1:什么是继承

继承发生前提:当多个类中有相同的属性和行为时,把这些相同的属性和行为,抽取出来封装一个独立的类中

在Java中使用继承(extends),来继承这些封装了公共属性和行为的类(父类)

继承:有父类、有子类


结论:让一个类(子类、派生类)继承另一个类(父类、超类、基类)

问题2:继承解决程序中的什么问题?

1、程序中代码的复用性(重复的代码抽取出来,封装到父类中,子类继承后就可以直接使用)
2、建立类和类之间的关系

问题3:继承怎么使用? //写代码

public class 父类{
    //共性成员
}

public class 子类 extends 父类{
    //子类可以使用父类中定义的内容
}

问题4:继承在使用中有使用注意细节

1、Java只能单一继承(不能多继承),允许多层次继承
2、在子类继承父类后,就直接可以父类中非私有的成员(成员变量、成员方法)
3、在继承中,访问成员变量或成员方法的原则:就近原则(先在子类中查找,子类没有再去父类中查找)
    super.父类中的成员变量
    super.父类中的成员方法
4、当父子类中存在一模一样的成员方法时,这种情况称为:方法重写
5、在继承中,要先初始化父类对象( 通过子类构造方法中的super()来调用父类的无参构造 )
   //调用父类有参构造方法: super(参数,....);

方法的体现:

  1. 重载
    • 在本类中发生
    • 方法名相同
    • 方法的参数列表不同(类型不同、数量不同、顺序不同)
    • 和返回值没有任何关系
  2. 重写
    • 发生在父子类(继承)中
    • 子类中的方法和父类的方法一模一样

方法重写:

  1. 什么是方法重写
  2. 方法重写解决什么问题
  3. 方法重写怎么实现
  4. 方法重写有什么注意细节

this关键字的使用:

//访问本类中的成员变量
this.成员变量名=数据值;

//访问本类中的成员方法
this.成员方法();

//访问本类中的构造方法(要求:只能使用某个构造方法,去调用本类中另一个构造方法)
public ClassName(){
    
}
public ClassName(String name){
    this(); //调用无参构造方法
}

学习套路:

  1. 这个技术是什么?
  2. 这个技术可以解决什么问题? (重点)
  3. 这个技术怎么使用? (重点)
  4. 这个技术在使用中的注意细节

回顾上午内容:

  • 继承

    • 继承是什么

      让一个类继承另一个类
      关键字:extends
      
    • 继承可以解决什么问题

      1、建立类与类之间的关系:父子关系
      2、可以解决程序中代码的复用性(子类可以直接使用父类中的非私有成员)
      
    • 继承怎么使用

      public class 父类{
          //成员
      }
      public class 子类 extends 父类{
          //子类可以直接使用父类中的非私有成员
      }
      
    • 继承在使用中的注意细节

      1Java语言只支持单一继承(不能多继承) , 允许多层次继承
      2、子类不能继承父类中的私有成员
      3、当子类和父类中存在相同的成员时,访问原则:就近原则
         super.成员变量 
         super.成员方法()
         super()
         super(参数, ...) 
      4、子类对象在实例化完成之前,必须先初始化父类对象(只有父类初始化完成后子类才可以访问父类成员)
         //子类构造方法在执行时,有隐藏代码:super()  //默认调用父类中的无参构造方法    
      
  • 方法重写

    • 什么是方法重写

      当父子类中存在一模一样的非私有成员方法时,就可以称为:子类重写父类的方法(方法重写)
      
    • 方法重写可以解决什么问题

      当父类中的方法,子类在调用时无法满足子类的要求,就可以在子类中重写父类的方法
      
      目的:
      1. 为后面学习多态准备
      2. 不愿意在起一个方法名
      
    • 方法重写怎么使用

      public class 父类{
          public void show(String name){
              ....
          }
      }
      
      public class 子类 extends 父类{
          //重写方法
          @Override
          public void show(String name){
              //重新编写方法体业务代码
          }
      }
      
    • 方法重写的注意细节

      1、针对父类中私有方法不能重写
      2、必须和父类中的方法一模一样
      

JVM内存划分:

  1. 栈 //方法运行在栈内存中
  2. 堆 //对象存储于堆内存中
  3. 方法区 //存放加载的.class文件 //方法区有由N多个不同功能的小区域组成
  4. 寄存器
  5. 本地方法区

this:当前类对象引用

this.成员方法()
this.成员变量 = 数据值
this() //本类无参构造方法
this("字符串")//本类带有String类型参数的构造方法    

super:父类对象引用

super.成员方法()
super.成员变量 = 数据值
super() //父类无参构造方法
super("字符串")//父类带有String类型参数的构造方法   
//父类:动物类
public abstract class Animal{
    //抽象方法:必须使用关键字abstract修饰
    //抽象方法:必须书写在抽象类中
    public abstract void eat();//在java语言中没有方法体的方法,称为:抽象方法 
}

普通类:

public class 类名{
    //成员变量
    private String name;
    
    //成员方法
    public void setName(String name){
        this.name = name;
    }
    
    //构造方法
    public 类名{
        
    }
}

抽象类:

public class 类名{
    //成员变量
    private String name;
    
    //成员方法
    public void setName(String name){
        this.name = name;
    }
    
    //构造方法
    public 类名{
        
    }
    
    //抽象方法
    public abstract void 抽象方法();
}
  • 抽象类,不能实例化(不能创建对象)
    • 疑问:既然不能创建对象了,为什么还要有构造方法?
      • 抽象类通常是用于父类,创建子类对象时,需要先初始化父类(抽象方法中构造方法的作用就是用于父类初始化使用)
  • 抽象类中的抽象方法,必须由子类重写

什么是设计模式?

  • 设计模式,就是一种解决方案(解决开发中某个问题的方案)

设计模式解决什么问题?

  • 在开发中一些常见问题,可以使用设计模式解决

模板设计模式:

  • 模板:就一个固定的格式
  • 模板设计:把确定的内容,提前准备好,把不确定的内容,定义为抽象方法(由子类重写)

创建对象:

Student stu = new Student(); //有名字的对象

//对象名: stu

匿名对象:

  • 没有名字的对象
new Student(); //创建的对象没有名字
new Student("张三",23);

回顾上次课程内容:

  1. 继承

    //什么是继承?
    让一个类继承另一个类就是继承。使用关键字:extends进行继承
    
        
    //继承解决程序中什么问题?(核心)
    1. 继承可以建立类与类之间的关系
    2. 继承可以提高程序中代码的复用性(子类在继承父类后,就可以直接使用父类中的非私有成员)    
    
        
    
    //继承怎么使用?(核心)
    public class 父类{
        
    }
    public class 子类 extends 父类{
        
    }
    
    
    //继承在使用中的注意事项
    1、继承只能单一继承(不支持多继承),允许多层次继承
    2、不能继承父类中私有的成员
    3、继承中成员的访问原则:就近原则(子类有中,就使用子类自己的,子类中没有,去父类中找)
    4、子类对象在初始化时,需要先初始化父类对象(只有父类初始化完成,子类才可以访问父类中的成员)
        问题:子类怎么做到在创建对象时,先把父类初始化?
        答案:子类的构造方法中,默认第一行都有隐藏代码super()//调用父类无参构造方法
    
  2. 方法重写

    //什么是方法重写?
    子类中存在和父类中一模一样的方法,称为:方法重写
    
    
    //方法重写解决程序中什么问题?
    当父类中的方法,不能满足子类的需求,子类可以重写父类中的方法
        
    
    //方法重写怎么使用?
    public class 父类{
        public void method(String name){
            
        }
    }
    public class 子类 extends 父类{
        @Override //和父类一模一样的方法
        public void method(String name){
            
        }
    }    
    
    
    //方法重写在使用中的注意事项
    1. 不能对父类中的私有方法进行重写
    2. 子类中的方法必须和父类中的方法一模一样    
    
  3. 抽象类

    什么是抽象类?
    
    
    //抽象类解决程序中什么问题?
    1. 当某个不希望被创建对象,可以使用abstract修饰为抽象类(仅仅只是做为父类使用)
    2. 当某些方法无法书写具体的方法体代码时,把这些方法修饰为抽象方法,书写在抽象类中    
    
    
    //抽象类怎么使用?
    public abstract class 抽象类名{
        
    }
    
    
    //抽象类在使用中的注意事项
    1、抽象类不能实例化(不能创建对象)
    2、抽象类比普通类多了一个抽象方法(普通类可以书写的内容,都可以书写在抽象类中)
       抽象类中可以书写构造方法(目的:为了保障子类对象创建成功) 
    3、抽象类中的所有抽象方法,在子类中都要重写(另一种:子类也做为抽象类)    
    
  4. 模板设计模式

    //模板设计模式解决程序中什么问题?
    当程序中有固定的执行流程了,但流程中的某个或某些点,在不确定时,就可以使用模板设计模式
        
        
    //模板设计模式的使用
    public abstract class 模板设计模式的抽象类{
        public void 固定执行流程的方法(){
            //1、固定流程格式
            
            //2、固定流程格式
            
            
            //3、不确定(定义为抽象方法)
            
            //4、不确定(定义为抽象方法)
            
            //5、固定流程格式 
            
            //6、不确定(定义为抽象方法)
        }
        
        public abstract 返回值类型 抽象方法();//3、不确定(定义为抽象方法)
        
        public abstract 返回值类型 抽象方法();//4、不确定(定义为抽象方法)
        
        public abstract 返回值类型 抽象方法();//6、不确定(定义为抽象方法)
    }    
    

static

什么是static

static是java语言中的修饰符关键字
用来修饰:成员变量、成员方法

static解决程序中的什么问题?

当程序中的某个数据需要共享时使用static修饰

static怎么使用?

public class Student{
    //成员变量
    private String name;
    private static String city;//静态成员变量
    
    //静态成员方法
    public static void method(){
        
    }
}

//通过类名访问静态内容(静态的私有成员变量不能访问)

static在使用中的注意细节

1、静态内容是随着类的加载就存在了(早于对象创建)
2、静态内容通常是使用: 类名.成员变量     类名.成员方法()
3、静态方法中只能访问静态成员(静态变量、静态方法)
4、静态方法中不能使用this关键字
5、非静态方法中可以直接访问:静态成员

JVM内存划分:

  1. 方法区
    • 是由N多个不同功能的小区域组成的
      • 静态区域:存放静态内容
      • 非静态区域:存放非静态内容
      • 字符串常量池:存放字符串常量
  2. 寄存器
  3. 本地方法区

final

final是什么

final是java语言中的修饰符关键字
用来修饰:类、变量、方法    
    
final关键字的含义:最终的、不可改变的    

final解决程序中什么问题

1、当某个变量不允许修改数据值时,可以使用final修饰变量
2、当某个方法不允许被子类重写时,可以使用final修饰方法
3、当某个类不允许被继承时,可以使用final修饰类。  举例:String类

final怎么使用

public final class 类名{
    
    private final int COUNT=100;
   
    public final void method(){
       
   } 
}

final在使用中的注意细节

final和abstract不能共存使用

权限修饰符

权限修饰符是什么

权限修饰符是java语言中的关键字,用于修饰:类、变量、方法
权限修饰符:private 、public 、protected、默认(什么都不写)

权限修饰符号解决什么问题

限制程序中的访问权限(类的访问权限、变量的访问权限、方法的访问权限)

权限修饰符怎么使用

//类(权限:公开的 [没有限制])
public class{
    //成员变量(私有权限:本类中使用)
    private String name;
    
    //默认权限(权限:当前包下的任意类)
    void show(){
        
    }
    
    //protected(权限:子类)
    protected void method(){
        
    }
}

从小到大排序:
1. private //访问范围: 仅现本类中访问
2. 默认     //访问范围: 只能在同一个包下的类
3. protected  //访问范围: 同一个包下的任意类 或 不同包下的子类 
4. public     //访问范围 :没有限制    

权限修饰符在使用中的注意细节

//方法重写时:保障子类中重写访问的访问权限必须 >= 父类中方法的权限

在开发中经常会使用或开发:工具类

  • 工具类的特点:
    1. 不能被继承
    2. 不能让其他类创建对象
    3. 提供静态方法
//final保障 :工具类不能被继承
public final class 工具类{
    
    //不能让其他类创建对象
    private 工具类(){}
    
    //给外部提供:静态方法
    public static 返回值 静态方法(...){
        
    }
}

问题:在一个类中可以书写哪些内容?

public class{
    //成员变量:静态、非静态
    
    //成员方法:静态、非静态、抽象
    
    //构造方法
    
    //代码块
    
}

代码块:

{
    
}

代码块划分:

  • 静态代码块 //开发中使用最多
    • 书写位置:在类中方法外(和成员变量、成员方法属于同一级)
      • 在构造代码块上添加:static
    • 特点:随着类的加载,执行1次静态代码块(类只会被加载1次)
    • 作用:用于对静态成员数据进行初始化
  • 构造代码块
    • 书写位置:在类中方法外(和成员变量、成员方法属于同一级)
    • 特点:在每个构造方法执行前,都会先执行构造代码块
    • 作用:当类中多个构造方法中有共性内容时,可能抽取到构造代码块
  • 局部代码块
    • 书写位置:写在方法体中
    • 特点:在方法执行时,才会调用
    • 作用:用于作用域

接口

接口的作用:制定规则

制定规则的两种方案:

  1. 定义类,类中书写抽象方法(不建议使用。 类只能单一继承)
  2. 定义接口, 接口中书写抽象方法(接口好处:可以多实现)

接口的定义:

public interface  接口名{
     //抽象方法
}

接口的特点:

  1. 接口不能实例化(不能创建对象)
  2. 接口中没有构造方法(接口只能通过子类来实现对象创建)
  3. 接口可以多实现( 一个类可以实现多个接口[解决类只能单一继承的问题] )

按照接口中定义的规则开发子类: 编写接口实现类

public class 接口实现类 implements 接口{
    //重写接口中所有的抽象方法
} 

接口的多实现:

public class 接口实现类 implements 接口1 , 接口2 , 接口3 {
    //重写所实现接口中所有的抽象方法
} 

在接口可以书写的内容:

public interface 接口名{
    
    //抽象方法
    
    //静态常量
    public static final int NUMBER = 10;
}
  • 思考:静态常量在程序中做什么呢?

    问题:生活中有没有一些数据是固定的,且值比较单一?
    示例:性别(固定、且仅有2个值)
    
    public interface Gender{
       public static final String BOY = "男";
       public static final String GIRL = "女";
    }
    
    //程序中需要给性别赋值
    String sex  = Gender.BOY;
    String sex = Gender.GIRL;
    
    
    

接口中的方法的升级:

  • 随着JDK版本的升级
    • 在jdk1.8版本中新增:默认方法、静态方法 //有方法体代码
    • 在jdk1.9版本中新增:私有方法 //有方法体代码

类和接口的概念:

类和类之间的关系: 继承 (只能单一继承)
public 子类 extends 父类{
    
}

类和接口之间的关系:实现
public 子类 implements 接口{
    
} 
    
   
接口和接口之间的关系:继承 (允许多继承)
public 接口1 extends 接口2 {
    //问题:为什么接口可以多继承?
    //答案:接口中仅仅只是定义功能(没有功能的实现,子类要自己实现功能)
}

接口和抽象类的相同之处:

  1. 接口和抽象类, 都不能实例化(不能创建对象)
  2. 接口和抽象类,都具有抽象方法
  3. 接口和抽象类,都是做为父类型存在的

抽象类和接口的区别:

  1. 抽象类:除了抽象方法外,还具有成员变量、构造方法、非抽象方法
  2. 接口:除了抽象方法外,只有常量 (JDK8:默认方法、静态方法)(JDK9:私有方法)

回顾上午内容:

  1. static关键字

    //static是什么?
    static是java语言中的修饰符关键字。用来修饰:变量、方法
        
    //static解决程序中的什么问题?
    static可以让数据进行共享(多个对象可以使用同一个数据)
     
    //static的使用
    public class{
        //静态成员变量
        public static int count;
        //静态成员方法
        public static void method(){
            
        }
    } 
    通过类名访问:静态成员
    类名.count = 100;
    类名.method();
    
    //static在使用中的细节
    1. static修饰的内容是随着类的加载而存在的(早于对象的创建)
    2. static修饰的内容是存储在方法区的静态区别下(仅此一份)
    3. static修饰的静态方法中,只能访问静态成员
    4. static修饰的静态方法中,不能使用this关键字
    5. 非静态方法中可以访问:静态方法、静态变量
    
  2. final关键字

    //final是什么?
    final是java语言中的修饰符关键字。用来修饰:类、方法、变量
        
    //final可以解决程序中什么问题?
    final修饰的类:不能被继承
    final修饰的方法: 不能被重写
    final修饰的变量: 初始化值后不能改变其值(常量)
        
    //final的使用
    public final class 类名{
        //变量
        final String name="初始化值";
        
        //方法
        public final void method(){
            
        }
    }
    
  3. 权限修饰符号

    //权限修饰符解决什么问题?
    限制程序中访问权限
    
    //权限修饰符
    private 、默认(什么都不写)、protectedpublic 
        
    private  : 仅限本类中使用  
    默认(什么都不写) : 同一个包下的任意类
    protected  : 同一个包下的任意类 、 不同包下的子类
    public    : 没有限制 
    
  4. 接口

    //接口的作用:
    制定规则 
        
    
    //接口的定义(语法)
    public interface 接口名{
        //抽象方法
        public abstract boolean login(String name, String password);
    }    
        
    //接口的实现(语法) 
    public class 接口实现类 implements 接口名{
        //重写:抽象方法
        public boolean login(String name , String password){
            ......
                
            return boolean类型的值;    
        }
    }
    
    //接口的特点:
    1、接口不能实例化(不能创建对象)
    2、接口中没有构造方法
    3、接口可以多实现(解决类只能单一继承的问题)
        实现类 可以在继承一个父类的情况下,还可以实现多个接口
        public class 子类 extends 父类 implements 接口A , 接口B{
            //重写所有抽象方法
        }
        
    
  5. 代码块

    代码块:局部代码块、构造代码块、静态代码块
        
    构造代码块作用:当类中构造方法里面有共性内容时,可以抽出到构造代码块中
                 每次构造方法执行前,都会先执行构造代码块
        
    静态代码块:
    static{
        //特点:静态代码块是随着类的加载执行(执行1次)
        //作用:初始化数据(数据必须在创建对象之前就初始化完成)
    }    
    

    工具类的书写规则:

  6. 工具类,修饰为final(不让继承)

  7. 工具类的构造方法,设置为private(不让其他类创建对象)

  8. 工具类中提供:静态方法(通过类名访问)

static关键字

  • 解决程序中什么问题?

    静态变量:共享数据
    静态方法:工具类中方法的编写
    
  • 怎么使用

    public class{
        static 类型 变量名;
      
        public static 返回值类型 方法名(参数, ..){
            
        }
    }
    
    类名.静态变量名=100;
    类名.静态方法名();
    
  • 细节:

    1. 静态内容是随着类的加载,存放在方法区的静态区域下(仅有一份)【早于对象创建】
    2. 静态方法中不能使用this关键字
    3. 静态方法只能访问静态内容(静态变量、静态方法)

final关键字

  • final修饰的类, 不能继承
  • final修饰的方法, 不能被重写
  • final修饰的变量, 初始化值后不能修改其值(变为常量)

代码块:

static{
    //在对象创建之前,初始化数据
}

接口:

  • 接口的作用:制定规则

  • 接口的使用

    • 接口定义(制定规则)

      public interfce 接口名{
          //常量
          public static final String 常量名 =;
          //抽象方法(制定规则)
          public abstract 返回值类型 方法名(参数, ...);
      }
      
    • 接口实现(按照规则实现代码)

      public 接口实现类 implements 接口名{
          //重写接口中的抽象方法
      }
      

问题:在继承时,子类可能把父类的构造方法继承了吗?

答案:不可以。

枚举:

  • 解决什么问题?

    当程序中的某个数据有固定在一定范围的取值时,为保障程序中数据的有效性,可以使用:枚举
    
  • 使用方式:

    public enum 枚举名{
        固定选项值1,固定选项值2,...;
    }
    
    枚举名.选项值;
    
    
    
    
    public enum Sex{
        BOY,GIRL;
    }
    
    public class Student{
        
        
        private Sex sex;
        
    }
    
    
    Student stu = new Student( Sex.BOY )
    

多态

什么是多态?

多态:多种形态

多种形态:
   同一个对象,在不同时刻表现出来的不同形态
   Cat extends Animal
   第一种形态: Cat  c1 = new Cat();  //c1是只猫
   第二种形态: Animal c2 = new Cat(); //c2是个动物
   
举例:
   数字2     
       10进制:2
       2进制: 00000010

多态解决程序中的什么问题?

1、提高代码的复用性
2、提高代码的扩展性


public interface Player{
    public void play();
    public void stop();
}

public MP3 implements Player{
    public void play(){
        System.out.println("mp3播放")
    }
    public void stop(){
        System.out.println("mp3停止")
    }
}
public MP4 implements Player{
    public void play(){
        System.out.println("mp4播放")
    }
    public void stop(){
        System.out.println("mp4停止")
    }
}


public static void main(String[] args){
    MP3 mp3 = new MP3();
    method(  mp3  )
    
    MP4 mp4 = new MP4();
    method( mp4 )
}

//多态的体现:  Player p = new MP3 / MP4  ();

public static void method(  Player   p     )// Player p = new MP4()  
{
    p.play();
    p.stop();
}



//集合: ArrayList类 implements List接口   、   LinkedList类 implements List接口
List list = new LinkedList();

多态的使用

//多态的代码体现格式:  
父类型  对象 =  new 子类();  //大类型包含小类型   int a=10;   long b = a;

多态在使用时的注意细节

  1. 多态中:父类和子类拥有一模一样的成员变量时:
    • 编译时:以父类型中的成员变量为主
    • 运行时:使用父类中的成员变量
  2. 多态中:父类和子类拥有一模一样的成员方法时:
    • 编译时:以父类中的成员方法为主(检查父类中有没有这个成员方法,没有:报错)
    • 运行时:以子类中的成员方法为主(调用子类对象中重写后的成员方法)

多态转型

  • 向上转型(自动类型提升)

    //基础班: int a =10;   long  num = a;  
    
    //多态:
    父类型  父引用 = new 子类(); //子类型变为:父类型 (弊端:父引用不能使用子类特有成员)
    
  • 向下转型(强制类型转换)

    //解决的问题: 父引用不能使用子类中特有成员
    
    //基础班: long a =100L;    int b = (int) a;
    
    //多态的向下转型:
    子类 对象 = (子类) 父引用;
    
    • 注意细节:

      • 向下转型时容易发生:java.lang.ClassCastException(类型转换异常)

        解决方案:instanceof

        if( 父引用对象  instanceof 子类型 ){
            子类型 对象 = (子类型)父引用对象;
            对象.特有成员
        }
        

多态代码的书写:

  1. 创建对象

    //父类型  父引用 = new 子类()
    Father f = new Son();
    
  2. 作为参数

    public void method( Father f )
    {
        f.方法()
    }
    
  3. 作为返回值

    public Animal getInstance()
    {
        //返回:子类对象
        return new Cat();
    }
    

内部类:

public class HelloWorld{
    //成员变量
    
    //成员方法
     
    //在一个类中,定义了另外一个类(内部类[成员内部类])   
    class Hello{       
    }
    
}

问题:如何访问某个类中的成员变量or成员方法?

答案:创建类的对象,通过"对象名.成员"的方式访问

匿名内部类:

  • 解决程序中什么问题?

    简化程序中代码的书写
    
  • 怎么使用?

    //语法格式:
    new 类名/接口(){
        //重写方法
    }
    
    //示例:
    public class Father{
        public String sayHello(String name){
            return "早上好, "+ name;
        }
    }
    //此时:程序的需求发生改变,要求sayHello方法的返回值为"你好,名字"
    
    public class Son extends Father{
        //重写sayHello方法
        public String sayHello(String name){
            return "你好, "+name;
        }
    }
    
    
    //以下代码使用匿名内部的方式(其实本质就是一个子类对象)
    new Father(){
        //把父类中的方法重写
        public String sayHello(String name){
            return "晚上好,"+name;
        }
    }
    

工具类:为解决某些特定问题,特意编写的公共类(程序员都可以使用)

  • JDK(java开发工具包) = JRE + 核心类库
    • 核心类库: jdk提供一些现成的功能,这些功能封装在不同的类中
  • JDK就提供了一些工具类:Math、Arrays
    • Arrays工具类:针对数组,提供了一系列现成的功能(例:排序)

回顾上午内容:

  • 多态

    //多态可以解决程序中什么问题?
    1、提高程序中代码的复用性
    2、提高程序中代码的扩展性    
    
    //多态怎么使用?
    //1、创建一个对象
    父类型 父引入 = new 子类(); 
    //2、作为参数
    public void method(父类型 父引用){
        父引用.成员方法()
    }
    //3、作为返回值
    public 父类型 getInstance(){
        return 子类对象;
    }
    
    //多态在使用中的细节
    父子类中有相同的成员变量时:编译和运行都以父类型中的成员变量为主
    父子类中有相同的成员方法时:编译时检查父类型中的成员方法是否存在  
                           运行时以子类对象中的重写的方法为主   
    
    //多态的转型
    向上转型: 父类型 父引入 = new 子类(); //弊端:父引用不能访问子类对象中特有成员
    向下转型: 子类  对象 = (子类) 父引用; //向下转换后,可以访问子类对象中特有成员
              在向下转型时容易发生:ClassCastException 异常
              使用:instanceof 避免   ClassCastException异常发生
                  if(父引用 instanceof 子类类型){
                      子类 对象 =(子类) 父引用;
                  }
    
  • 匿名内部类

    //匿名内部类解决什么问题?
    简化程序中的代码
        
    //匿名内部类的使用
    父类型 父引用 = new 父类/接口(){
        //重写父类型中的方法
    };  
    
    //作为参数
    public void show( Flyable  fly );
    
    show( new Flyalbe(){
        //重写方法
    } );
    
    
    
    //作为返回值
    public Flyable getInstance(){
        return new Flyable(){
           //重写方法  
        };
    }
    

在Java继承体系中,有一个顶层父类:Object(鼻祖类)

  • 在java语言中,程序员自定义的类或者jdk中存在的类,直接或间接都要继承Object
  • 继承了Object类后,就可以使用Object提供的相关方法:
    • boolean equals(Object obj)
      • boolean: 方法的返回值类型
      • Object: 方法传递参数的类型
      • 作用:比较两个对象是否相同(比较两个对象的地址值是否相同)
    • String toString()
      • 作用: 把对象转换为字符串

比较两个字符串是否相同 :

//方案1: String类中的equals方法
boolean result = 字符串1.equals(字符串2);

//方案2:Objects工具类中的equals方法
boolean result = Objects.equals(字符串1 , 字符串2);


区别:Objects工具类中的equals方法 可以避免NullPointerException异常的发生

日期类型的数据:

public class Emp
{
    //姓名
    private String name;//String表示字符串
    //年龄
    private int age;//int表示整数
    //生日
    private Date birthday;//Date表示日期
    
}

Date类:

  • 归属包:java.util

  • 作用:表示日期时间(精确到毫秒)

  • 使用:

    • 构造方法(创建对象)

      public Date()
      public Date(long time)
      
    • 常用方法

DateFormat类

//DateFormat类是什么
DateFormat类是java语言提供的日期格式化类
    
//DateFormat类解决什么问题
对日期进行格式化操作:Date类型 -> String类型     =>  String format(Date)
对日期进行解析操作:String类型 -> Date类型      =>  Date parse(String )

//DateFormat类怎么使用
    java.text.DateFormat
    DataeFormat是一个抽象类,无法实例化。使用子类:SimpleDateFormat
DateFormat df = new SimpleDateFormat("日期模板");
    //日期模板:就是使用一些特定的字符,来表示某个日期。例:yyyy表示全年 MM表示月 dd表示日

DateFormat df = new SimpleDateFormat("yyyy/MM/dd");
DateFormat df = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");

String format(Date date) //将Date类型的数据,按照日期模板,转换为字符串
Date parse(String s) //将日期格式的字符串数据,按照日期模板,转换为Date对象    

Calendar类:日历类(存储的有日期、时间、日历信息)

  • 代替java.util.Date类中的过期方法
//Calendar类的使用: 构造方法、 常用方法
//Calendar类是一个抽象类,无法创建对象。    子类:GregorianCalendar

//在Calendar类中,提供了一个静态方法,该静态方法可以实现:Calendar对象的实例化(底层还是使用GregorianCalendar子类)

//获取Calendar类对象
Calendar c = Calendar.getInstance();


Calendar对象中存储的内容:

java.util.GregorianCalendar[
time=1667208388632,
areFieldsSet=true,
areAllFieldsSet=true,
lenient=true,

zone=sun.util.calendar.ZoneInfo[id="Asia/Shanghai",

offset=28800000,dstSavings=0,useDaylight=false,transitions=31,lastRule=null],

firstDayOfWeek=1,

minimalDaysInFirstWeek=1,ERA=1,

YEAR=2022,
MONTH=9, //月份:0~11
WEEK_OF_YEAR=45,

WEEK_OF_MONTH=6,

DAY_OF_MONTH=31,

DAY_OF_YEAR=304,
DAY_OF_WEEK=2,DAY_OF_WEEK_IN_MONTH=5,AM_PM=1,

HOUR=5,
HOUR_OF_DAY=17,

MINUTE=26,

SECOND=28,

MILLISECOND=632,

ZONE_OFFSET=28800000,DST_OFFSET=0]

获取Calendar对象中存储的数据:

//API方法:  public  int  get(int 字段名)  //字段名:YEAR     MONTH    DAY_OF_MONTHCalendar类中有一些静态常量: YEAR     MONTH    DAY_OF_MONTH
Calenar.YEAR       Calendar.MONTH
    
//获取日历对象中的年
Calendar c = Calendar.getInstance();
int year = c.get( Calenar.YEAR )

给日历对象设置具体的:年月日时分秒…

//获取日历对象中的年
Calendar c = Calendar.getInstance(); //日历对象是基于当前系统日期时间获取的

//修改日历对象中的信息     public void set(int 字段 , int 数值)
c.set( Calendar.YEAR , 2000 );

//修改日历对象中的年月日
c.set( 2000, 9, 31 );

给日历对象中的信息,增加或减少

public void add(int 字段  , int 数值) //数值是负数是减少 , 数据是正数表示增加

选择语句:

switch( 常量表达式 ){ //只能使用:byte、short、int、char、String、枚举
    case 常量值1:
        语句1;
        break;
    ...    
}

枚举

//什么是枚举
在java语言中,枚举是一种数据类型。枚举用来表示:固定且仅有几种取值范围的数据
例:性别
    
public interface Gender{
    public static final String BOY="男";
    public static final String GIRL="女";
}    
    
public class Student{
    private String name;
    private String sex;
    public Student(String name , String sex){
        this.name = name;
        this.sex = sex;
    }
}
Student stu = new Student("熊大", Gender.BOY);

Student stu = new Student("熊二", "男女");//不会报错
解决方案: 枚举类型


//枚举解决程序中的什么问题?
当程序中有数据是固定且只有几种取值范围时,使用枚举类型强制设置值的范围(赋值数据有保障)    
    

//枚举怎么使用?
public enum Gender{
    BOY , GIRL;
}
public class Student{
    private String name;
    //使用枚举类型
    private Gender gender;
    
    public Student(String name , Gender gender){
        this.name = name;
        this.gender = gender;
    }
}
Student stu = new Student("熊二", "男女");//报错。原因:性别类型必须是Gender枚举

Student stu = new Student("熊二", Gender.BOY);//正确(保障数据的合法有效性)

    
    

//枚举在使用中的注意细节
1. 枚举本质是一个类
2. 在枚举中除了可以书写:固定选项值外,还可以书写:成员变量、私有构造方法、成员方法

Math工具类

Math类:针对数字运算提供一系列方法

工具类的编写规则:

  1. 不能被继承,使用final关键字修饰类
  2. 不能创建对象,使用private关键字修饰构造方法
  3. 对外提供静态方法(通过类名访问)

BigInteger

问题:long类型在存储数据时有上限吗?

答案:是有上限

问题:在程序中使用long无法存储整数数字时,怎么办?

答案:使用BigInteger

学习API的套路:

  1. 明确API类归属包
  2. 明确API类可以解决什么问题
  3. 使用API
    • 构造方法
    • 常用方法
import java.math.BitInteger;

//使用构造方法,创建对象
BigInteger num1 = new BigInteger("1231231231231231231231");
BigInteger num2 = new BigInteger("1000000000000000");

//四则运算
BigInteger result1 = num1.add( num2 );//加运算

BigInteger result2 = num1.subtract( num2 );//减运算

BigDecimal

BigDecimal类:用来存储超出double范围的小数

import java.math.BigDecimal;

//使用构造方法,创建对象
BigDecimal num1 = new BigDecimal("12312312312312311.123123123123");
BigDecimal num2 = new BigDecimal("1000000000000000.9999999999");

//四则运算
BigDecimal result1 = num1.add( num2 );//加运算

BigDecimal result2 = num1.subtract( num2 );//减运算

Arrays工具类

Arrays类:针对数组提供了一系列方法

  • 数组元素排序(默认升序)
  • 数组转换为字符串

包装类

什么是包装类?

Java语言: 万物皆对象
    
Java8种基本类型,进行了封装,变为包装类    
基本类型包装类
byteByte
shortShort
intInteger
longLong
floatFloat
doubleDouble
charcharacter
booleanBoolen

包装类解决程序中的什么问题?

1、实现String类型和7种基本类型(没有char)之间的转换
   String str = "1234";       =>    int类型

2、在后面学习mysql时会大量使用到包装类(包装类中可以存储null数据)

包装类怎么使用?

//使用Integer作为代表

Integer num = 100;
Boolean flag = true;
Double num2 = 3.14;
Integer num = null;


//使用包装类:解决String类型和基本类型之间数据转换

//基本类型数据 => String类型
int num =100;
String str = num+""; //任意类型和字符串相加,结果都是String类型

String str = String.valueOf( num );



//String类型 => 基本类型数据
String str ="1234";
int num =  Integer.parseInt( str );

String s = "true";
boolean flag = Boolean.parseBoolean( s );

包装类在使用中的细节

  1. 在把String类型数据,转换为基本类型数据时,容易发生:NumberFormatException异常
    • 原因:String类型数据,不符合要转换的基本类型数据格式
  2. 包装类在使用时存在:自动装箱、自动拆箱
    • 自动装箱 : Integer.valueOf( 数值 )
    • 自动拆箱: Integer对象.intValue()
  3. 包装类中有常量池(数据值没有超出常量池范围,就直接从常量池中获取对象)

正则表达式

什么是正则表达式?

由一些特定的字符组成的字符串校验规则,称为:正则表达式

[1-9][0-9]{4,14}

正则表达式解决程序中什么问题?

正则表达式只能针对字符串进行格式校验
    
正则表达式在开发中的应用场景: 对用户输入的字符串数据进行校验(数据合法性。例:手机号码格式)

正则表达式怎么使用?(开发中基本都是CV)

//课程学习正则表达式的语法

回顾上午内容

Math类: 针对数值进行提供了一些数学运算功能
    
System类: 获取毫秒值
    
在开发中,如果整数的范围超出long类型后,使用:BigInteger, 
            小数的范围超出double类型后,使用:BigDecimal
 
Arrays类: 针对数组提供了一些功能(例:排序)
Arrays.sort( 数组 ) //默认排序方式:升序
String  s = Arrays.toString( 数组 );//把数组转换为字符串。  格式:"[元素,元素,...]"              
                

包装类:
    基于8种基本类型,进行封装,封装为类(包装类)
    int  Integer
    
    作用:实现String类型和基本类型之间的转换
    int num  = Integer.parseInt( "1234" );

    Integer num = 10;//自动装箱
    num++; //自动拆箱


正则表达式:
    针对字符串进行校验,校验字符串格式是否符合规则。

   
    qq号码验证: 5~15位、首位不为0、 全部是数字
    [1-9]\d{4,14}

集合

三种集合:List 、Set 、Map

集合有什么好处?

  1. 大小可变(随意扩容)
  2. 集合中可以存储多种不同类型数据
  3. 底层使用数据结构(存取效率高)

集合:不能存储基本类型数据

ArrayList list = new ArrayList();
list.add(100);//自动装箱: list.add( Integer.valueOf(100) )

Collection集合: 是一个接口

  • 子接口: List 、 Set
    • 子类: ArrayList

ArrayList集合对象可以使用传统的for循环,进行遍历

  • ArrayList集合有索引

Collection集合: 没有索引。 遍历集合使用专用方式:迭代器

迭代器

迭代器:Iterator

  • 作用:用于集合的遍历

Iterator迭代器中常用方法:

boolean  hasNext();   //检查迭代器对象中是否有下一个元素
Object  next(); //利用迭代器,取出下一个元素

Iterator迭代器在使用中的细节:

  1. 在迭代器完成集合的遍历后,不要在使用:next()方法
  2. 在迭代器遍历集合的过程中,不能使用集合对象来增删元素
    • 删除元素: 使用迭代器对象中的remove方法实现
    • 增加元素: 不用考虑(解决方案:使用其他的集合对象。例:List集合)

增强for循环

什么是增强for?

  • 就是基于普通的for循环,进行强化(底层使用:迭代器)

增强for的应用:

  • 针对数组或集合,进行遍历

增强for的语法:

for( 元素类型 变量 : 容器 )
{
    //从容器中获取出一个元素,赋给变量
    
    //下次:继承从容器中获取一个元素, 赋给变量
    
    //......
    
    //停止条件 : 容器没有元素了
}

BigDecimal类存在的问题:精度丢失

BigDecimal num = new BigDecimal( 100 ); //会有精度丢失的问题

BigDecimal 大浮点对象 = new BigDecimal( "100" ); //推荐使用方式


BigDecimal  返回值 = 大浮点对象.add( BigDecimal 参数 )

迭代器的使用步骤:

//1、通过集合对象,获取迭代器对象
Iterator  it  = 集合对象.iterator();

//2、使用迭代器对象中的API方法,获取集合中的每一个元素
while( it.hasNext() ){
    //取出每一个元素
    Object obj = it.next()
}

在开发中为了方便程序员的书写,把迭代器进行简化: 增强for

//增强for底层 :还是迭代器

for(元素类型 变量 : 集合){
    
}

今天课程:

  1. 泛型
  2. 数据结构
  3. List集合、Set集合

泛型

泛型是什么?

在java语言中,是一种类型参数,可以设置存储数据的类型

泛型解决程序中什么问题?

在创建集合对象时,明确了集合中所存储元素的类型(限定类型)
  • 泛型在使用在代码编写时期的技术方式(编译期技术)
  • 泛型在程序运行后,就擦除

泛型的使用

  • 泛型类

    public class 类名<T>{
        
    }
    
    //当不确定类中的某个属性使用什么类型时,可以用泛型表示
    public class 泛型类<T>{
        
        private T 变量;
    }
    
    //在创建泛型类对象时,明确类型
    泛型类<Integer> 对象 = new 泛型类<Integer>();//泛型类中成员变量的类型为:Integer
    
    泛型类<String> 对象 = new 泛型类<String>();//泛型类中成员变量的类型为:String
    
    
  • 泛型接口

    public class 接口名<T>{
        
    }
    
    //当不确定接口中的某个方法参数使用什么类型、或方法的返回值使用什么类型时:可以用泛型表示
    public interface 泛型接口<T>{
        public void method(T a);
    }
    
    //情况1:在子类编写时,指定接口上泛型的具体类型
    public class 子类 implements 泛型接口<String>{
        //方法重写
        public void method(String a){
            
        }
    }
    //情况2:在子类编写时,没有指定接口上的泛型。 意味着:子类也使用和接口相同的泛型了(子类:泛型类)
    public class 子类<T> implements 泛型接口<T>{
        //方法重写
        public void method(T a){
            
        }
    }
    子类<Integer> 对象 = new 子类<>();//创建子类对象时,明确了泛型的类型
    
  • 泛型方法

    //语法格式
    修饰符号 <泛型> 返回值类型 方法名( 泛型 参数1 , ...){
        //方法体
    }
    
    //当前类没有声明为泛型类,但该类中的方法参数或方法返回值不确定类型时: 使用泛型方法
    public <T> void method(T param){
        
    }
    //当调用方法时,向方法中传递参数的类型,就是泛型的类型
    
    
    

泛型中的通配符: ? (任意类型)

  • 通常在开发中,?是和泛型的上下限一起使用

泛型的下限

//指定泛型中的最小类型
<? super 最小类型>
    
? 可以是最小类型
? 可以是父类型    

泛型的上限

//指定泛型中的最大类型
<? extends 最大类型>
    
? 可以是最大类型
? 可以是子类型    

你们认为什么是数据结构?

数据结构:就是一种存储数据的排列方式

常见的数据结构:

  1. 栈(先进后出)
  2. 队列(先进先出) //数组就是这种方式
  3. 链表
  4. 哈希表
    • 二叉树
    • 平衡二叉树
    • 红黑树

队列数据结构: (队列是一种线性结构)

  • 特点:先进先出
  • 数组

回顾上午内容:

  • 泛型

    1. 泛型是什么

      泛型是一种类型参数,可以设置存储的类型
      
    2. 泛型解决程序中的什么问题

      在创建集合时,使用泛型约束所存储的元素类型。好处:在使用时不需要强制转换    
      
    3. 泛型怎么使用

      //泛型在使用时的划分: 泛型类、泛型接口、泛型方法
      
      //泛型类: 当类中的某个属性类型不确定时,可以使用泛型来表示
      public class 类名<E,T>{
          private String name;
          private E param;//不确定属性的类型
          
          public void method(T arg){       
          }
      }
      类名<String,Integer> 对象 = new 类名<>();
      对象.method( Integer类型的参数 );
      
      
      //泛型接口: 当接口中某个方法的参数或返回值不确定类型时,可以使用泛型来表示
      public interface 接口名<T>{
          public void method(T param);
          public T show(T args);
      }
      class 子类 implements 接口名<String>{
          //子类在实现接口时,明确了接口上泛型的类型(String类型)
      }
      class 子类<T> implements 接口名<T>{
          //子类继续延伸使用接口上的泛型
          //子类就变为: 泛型类
      }
      
      
      //泛型方法(提前:非泛型类):当类中的某个方法的参数不确定或返回值不确定时,使用泛型方法
      public class 类名{
          //泛型方法
          public <T> void 方法名(T 参数名){
              
          }
          public static <T> T 方法名(T 参数名){
              
          }
      }
      String 结果 = 类名.方法名( String类型 ) 
      
    4. 泛型在使用中的注意细节

      • 泛型不支持多太态

        ArrayList<Person> list = new ArrayList<Student>(); //报错
        
      • 泛型在语法上支持:通配符 ? //表示任意类型

        ArrayList<?> list = new ArrayList<Student>();
        
      • 泛型中的通配符,通常会配置上下限使用

        //泛型上下限,通常用于方法定义时
        
        //上限
        public void method(ArrayList<? extends 父类型>  list){
            //传递的list集合对象中存储的元素必须是:父类型 或 其子类型
        }
        
        //下限
        public void method(ArrayList<? super 子类型>  list){
            //传递的list集合对象中存储的元素必须是:子类型 或 其父类型
        }
        
  • 数据结构:栈、队列

    什么是数据结构?
    存储数据时,底层对数据排列的方式
    
    常见数据结构: 栈、队列、数组、链表、哈希表、树
    
    计算机底层最原始的数据结构仅有两种: 线性结构、非线性结构
    
    栈特点:先进后出
    队列特点:先进先出
    

数组结构:

  • 数组在内存中的体现是一块连续存储数据的空间

  • 查询快

  • 增删元素效率慢

  • 在已学习的ArrayList集合,底层就是使用:数组结构

ArrayList集合的特点:查询快、增删慢

List集合

Java语言中集合体系划分:

  • Collection (接口)
  • Map (接口)

java.util.Collection集合: 是一个接口(无法实例化)

  • java.util.List集合(接口)
    • 常用子类:ArrayList 、 LinkedList
  • java.util.Set集合(接口)

java.util.List集合:

  • 带有索引
  • 存储的元素的顺序和获取元素的顺序一致
  • 可以存储重复元素

因为List集合可以使用索引,故围绕着索引,设计很多API方法:

//添加元素
List集合.add( 索引 , 元素值) //向List集合中指定索引位置上,添加元素
                           //如指定索引位置上已有索引,会自动后向移动

//修改元素
List集合.set( 索引 , 元素值) //修改List集合中指定索引位置上的元素值    

//删除元素
List集合.remove(索引) //删除List集合中指定索引位置上的元素值    

//获取元素
List集合.get(索引);

HashMap集合

ArrayList集合:

  • 实现List接口(List接口中的所有功能都有)
  • 底层使用:数组
    • 查询快 、 增删慢

LinkedList集合:

  • 实现List接口

  • 底层使用:双向链表(有头有尾)

    • 增删快 、 查询慢
  • 特有方法都围绕着链表头和尾设计

    //添加元素
    addFirst( 元素 ) //把元素添加到链表头部
    addLast( 元素 )  //把元素添加到链表尾部    
    
    
    //删除元素
    removeFirst()
    removeLast()    
    
    
    //获取元素
    getFirst()
    getLast()    
    
    

链表结构:

  • 在内存中是使用节点来存储数据

    • 节点 = 数据 + 地址
  • 链表:有头、有尾

  • 分类:

    1. 单向链表:只能从头到尾
    2. 双向链表:可以从头到尾,也可以从尾到头 (提高查询效率)
  • 代表集合类:LinkedList

Collection集合

  • List集合

    1. 有索引
    2. 存取元素有序
    3. 可能存储重复元素
  • Set集合

    1. 没有索引
    2. 存取元素不保证顺序
    3. 不允许存储重复元素

Set集合中方法全部来自Collection集合

Set集合的子类:

  • HashSet集合

    • 特点:

      1. 没有索引
      2. 存取元素不保证顺序
      3. 不能存储重复元素
    • 底层使用:哈希表结构

  • LinkedHashSet集合

  • TreeSet集合

哈希表数据结构:

  • 底层是使用大小为16的数组+链表组成的存储方式

  • 哈希表存储数据的方式 ( 借助:哈希值[存储位置] )

    拿要存储的元素,结合哈希算法, 计算出哈希值(存储位置) // 调用:  元素.hashCode()
    
    判断 : 计算出的存储位置上是否有元素存在    
           情况1 : 没有元素存在     =>  直接存储
           情况2 : 已有元素存在
                   拿要存储的元素 和 已经存在的元素 进行比较(比较内容是否相同) //元素.equals() 
                   相同: (重复元素)  =>  不存储
                   不相同: (不重复元素)
                          再次拿当前存储空间作为算法因子,再次进行哈希算法,计算新的存储空间
                          
    
    
  • 从JDK1.8开始,哈希表底层进行优化,使用:数组+链表/红黑树

    • 从链表 => 红黑树时机: 当链表的长度>8,自动 把链表转换为红黑树

Set集合:

  • HashSet
    • 底层:哈希表结构
    • 特点:
      1. 不能存储重复元素
      2. 没有索引
      3. 存取元素顺序不保证一致
  • LinkedHashSet
    • 特点:
      1. 没有索引
      2. 不能存储重复元素
      3. 存取元素顺序一致
    • 底层:哈希表+链表(保证存储元素的顺序)
  • TreeSet

今天课程优先级:

  1. Map集合
  2. TreeSet集合
  3. 可变参数

Collection(接口)

  • List(接口)

    • 特点:

      1. 有索引
      2. 存取元素有序
      3. 允许存储重复元素
    • 常用子类:

      • ArrayList

        • 特性:
          1. 底层使用数组结构
          2. 查询快、增删慢
          3. 具有List集合中的特点
      • LinkedList

        • 特性:

          1. 底层使用双向链表结构(有头有尾)

          2. 增删快 、查询慢

          3. 具有自己的特有方法(围绕着链表的头和尾设计)

            Object getFirst();
            Object getLast();    
            
  • Set(接口)

    • 特点:
      1. 没有索引
      2. 存取元素不保证顺序
      3. 不允许存储重复元素
    • 常用子类:
      • HashSet
        • 特性:
          1. 底层使用哈希表结构(哈希表去重原理:存储的元素必须重写hashCode、equals方法)
          2. JDK1.8底层优化了哈希表(当链表长度超出8,就自动转为红黑树)
      • LinkedHashSet
        • 特性:
          1. 底层使用哈希表+链表的结构
          2. 哈希表用来去重、链表用来保证存取元素的顺序
      • TreeSet
        • 特性:
          1. 底层使用树(红黑树)结构
          2. 不能存储重复元素
          3. 不具备索引
          4. 存储的元素会按照规则进行排序

泛型:

//在开发中使用泛型最多的就是:创建集合时使用泛型约束所存储元素的类型
List<Student> studentList = new ArrayList<>();

TreeSet集合:

  • 具有对所存储元素进行排序的功能

在TreeSet集合中存储:String、Integer、Double、Character //JDK提供的类型

  • 都默认已实现了java.lang.Comparable接口 (自带自然排序规则)

在TreeSet集合中存储:自定义类型 //程序员自己定义的

  • 就必须保证自定义类型,有实现java.lang.Comparable接口,并重写compareTo方法
  • 如果自定义类型,没有实现Comparable接口,在存储到TreeSet集合中时,会引发异常

compareTo方法:

//比较大小:  0表示相同  、 负数表示小    正数表示大
public int compareTo(E e){ 
    自然排序规则
    
    
    //返回值有三种: 正数 、 负数 、 0  (底层红黑树需要)
}

二叉树:

  • 以根节点为坐标,小于根节点的数据存储在左边,大于根节点的数据存储在右边

TreeSet集合:

  • 底层使用:红黑树
    • 去重、排序(拿树中已存在的元素 , 和要存储的元素进行比较[比较大小:0、正数、负数] )
  • 存储自定义元素,要保证自定义元素有实现java.lang.Comparable接口

JDK提供的:String、Integer 等类,都已具备自然排序(实现Comparable接口)

  • String类默认就已有排序规则(按照字典顺序从小到大排序)

需求:在TreeSet中存储的String类型数据,按照字符串长度从大到小排序

  • 结论:使用自然排序做不到(String类是final修饰,不能继承)
  • 解决方案:不使用自然排序,使用其他排序方式(比较器)

排序方式:

  1. 自然排序: 元素必须实现Comparable接口
  2. 比较器排序: 元素不需要实现Comparable接口(需要在创建TreeSet对象时,指定排序规则)

TreeSet构造方法:

public TreeSet() //默认使用自然排序
public TreeSet( Comparator c ) //指定比较器对象    

Comparator接口(泛型接口): 比较器

int  compare(Object obj1 , Object obj2) //比较两个元素的大小: 0、正数、 负数

回顾上午内容:

  • TreeSet集合

    • 特点:

      1. 底层使用红黑树结构
      2. 不能存储重复元素(去重)
        • 拿要存储的元素和树结构中已经存在的元素进行比较:
          • 0:重复元素
          • 负数:向树的左边
          • 正数:向树的右边
      3. 没有索引
      4. 存储的元素会按照指定规则进行排序
    • 排序规则:

      1. 自然排序

        • 元素自身要具备排序方式(要实现Comparable接口)
          • JDK中的提供的现成类:String、Integer、… 都自带自然排序
          • 程序员自定义的类,要实现Comparable接口(具备自然排序)
        TreeSet<Integer> set = new TreeSet<>();
        set.add(100);set.add(99);set.add(200);set.add(10);
        
        
        TreeSet<Student> set = new TreeSet<>();
        set.add(学生对象1);
        set.add(学生对象2);
        
        //自定义类实现Comparable接口
        public class Student implements Comparable<Student>{
            public int comparaTo(Student stu){
                
                
                return 0 / 正数  / 负数;
            }
        }
        
      2. 比较器排序

        • 元素不需要具备任何排序方式
        • 在创建TreeSet对象时,指定比较器对象(排序规则)
        //创建集合对象时,指定比较器
        TreeSet<Student> set = new TreeSet<>( new Comparator<Student>(){
            //重写方法
            public int compare(Student stu1 , Student stu2){
                //stu1 : 要存储的元素
                //stu2 :  已存在的元素
            }  
        } );
        
        
        //不需要实现任何接口
        public class Student{
            //成员变量
            //成员方法
        }
        

可变参数:

  • 在java语言提供了一种特殊的参数:可变参数(可以改变的参数)

    • 在调用方法时,传递的参数可以是任意个(底层:是使用数组)
  • 语法格式:

    public 返回值类型 方法名(参数类型... 参数名){
        //... 就是可变参数的语法表示形式
    }
    

Collections工具类:

  • 不能创建对象
  • 提供了静态方法
  • 针对List、Set集合进行相关操作(排序、二分查找、添加元素、…)

三大集合:List、Set、Map

  1. List (单列集合: 集合在存储元素时,一次只能存储一个元素)

  2. Map (双列集合:集合在存储元素时,一次存储两个元素[key元素 、 value元素])

  3. Set (单列集合)

Map集合

map集合的特点:

  1. 可以存储两个元素(键值对元素)
  2. key元素不能重复, value元素允许重复
  3. 一个key元素只能对应一个value元素(一一对应)//通过key可以找到value
  4. 存取元素不保证顺序
  5. 没有索引

java.util.Map(接口)

  • HashMap:底层使用哈希表
  • LinkedHashMap:底层使用哈希表+链表
  • TreeMap:底层使用红黑树

Map集合不能直接遍历(只能间接性实现遍历操作)

Map集合遍历方式:

  1. 键找值

    • 获取Map集合中所有的key元素,遍历所有的key元素,通过key元素找到对应的value元素
    Set  存储所有key的Set集合对象 =  map集合.keySet();
    
  2. 键值对

    • 获取Map集合中所有的Map.Entry, 遍历所有的Map.Entry,通过Entry中的API方法获取到key、value
    Set<Map.Entry>  存储所有键值对对象的Set集合对象 =  map集合.entrySet();
    
    Map.EntryObject  getKey()
        Object  getValue()
    

在Map集合中当key存储的是自定义对象时,要保证对象存储数据的唯一性,需要:自定义对象中重写hashCode、equals方法

TreeMap集合

  • 特点:存储的key元素,会按照指定规则排序
    • 排序方式:
      1. 自然排序: 元素自身实现Comparable接口
      2. 比较器排序:在创建集合对象时,指定比较器
TreeMap<String,String> map = new TreeMap<>(); //String类要具备自然排序
TreeMap<Student,String> map = new TreeMap<>();//Student类要具备自然排序

TreeMap<Student,String> map = new TreeMap<>( Comparator对象 );//指定比较器对象 

Map集合:(双列集合)

  • 一次存储两个元素(key、value)
    • key不能重复、value可以重复
    • 一个key只能对应一个value
  • 不能直接遍历(间接性遍历)
    • 键找值:先获取Map集合中所有的key元素,遍历所有的key元素,在遍历过程中通过key找到value
    • 键值对:先获取Map集合中所有的键值对对象(Map.Entry),遍历所有的键值对对象,在遍历过程中获取到key和value

嵌套for循环:

for()
{
    for()
    {
        
    }
}
  • 可以应用于:二维数组遍历、嵌套集合遍历

    int[][] array = {  {1,2,3},  {4,5} };
    
    for(int i=0;  i<array.length;  i++)
    {
        //取出每一个元素(一维数组)
        int[] arr = array[i];
        //{1,2,3}
        // {4,5}
        
        for(int j=0;  j<arr.length  ;j++)
        {
            int num = arr[j];
        }
        
    }
    
    list1[  list2[1,2,3] , list3[4,5]  ]
        
    //遍历:list1
        //取出每一个元素:list2、list3
        //遍历 : list2 、list3
    

Arrays工具类:是针对数组提供了一些功能(排序、转换为字符串)

Collections工具类: 是针对集合提供了一些功能(排序、二分查询、乱序、添加另一个集合)

//把给定的元素添加到指定的集合中
Collections.addAll( ListSet集合 ,  元素1,元素2,...... );
Collections.sort( 集合 );//对集合中的元素进行排序(自然排序、比较器)

Collections.shuffle(  集合 );//对集合中的元素随机打乱顺序

使用java语言开发完成的程序,要给其他人使用,怎么办呢?

  • 把程序打包: xxxx.jar (jar文件是java文件压缩包)

要使用第三方开发好的程序,怎么办?

  • 在自己的项目工程下,导入xxx.jar包

使用Logback的步骤:

  1. 导入Logback所需的相关jar文件,并添加到项目资源库中
    • 在项目工程下新建lib文件夹,把logback需要的jar文件存放到该文件夹下
    • lib目录下的存储的jar文件,添加到当前项目资源库中
  2. 把logback核心配置文件,拷贝到当前项目的src目录下
  3. 在类中获取到Logger日志对象,使用日志对象中的API方法记录需要的操作信息

Logback日志框架的核心:logback.xml

  • xml文件

Logback日志级别(从大到小):

  1. error //错误
  2. warn //警告
  3. info //信息
  4. debug //调试
  5. trace //追踪(例:追踪用户行为轨迹)

在这里插入图片描述
在这里插入图片描述
logback使用步骤:

  1. 在当前项目下导入logback相关jar文件,并添加到项目工程资源库中
  2. 把logback核心配置文件,复制到当前项目工程的src目录下
  3. 在开发的类中,创建logback对象,使用logback中的API方法记录日志信息

logback日志级别:

  1. trace //追踪
  2. debug //调试(程序有bug,测试bug使用的)
  3. info //关键信息
  4. warn //警告
  5. error //错误

异常

什么是异常?

  • 程序在运行过程中,发生了不正常的情况,造成程序中断向下执行

有异常了怎么办呢?

  • 当前发生异常的代码位置处,有处理异常的代码,那么异常就会被处理掉,程序继续执行
  • 当前发生异常的代码位置处,没有处理异常的代码(程序中没有处理异常的代码),最终异常会抛出给JVM来处理
    • JVM处理异常的方式:
      1. 打印异常的信息(异常的类型、异常发生的位置、异常的原因)
      2. JVM停止

异常处理的方式:

  1. 声明:throws
    • 遇到异常发生了,自己不处理,交给别人处理
      • 最终还是需要有一个位置使用try…catch来处理异常
        • 结论:在main方法中,只能使用try…catch
  2. 捕获:try…catch
    • 遇到异常发生了,自己处理

使用声明的方式处理异常:

//使用声明的方式处理异常,声明是书写在方法定义上
修饰符号  返回值类型 方法名(参数类型 参数,...) throws 异常类1 , 异常类2.... 
{
    
}
//在定义方法时,使用声明的方式处理异常
public void method(String name) throws NullPointerException {
   //
}

Java基础面试题 : throws和throw的区别

  • throws : 声明

  • throw : 抛出 (手动引发异常)

    • 程序员手动创建一个异常对象,并招抛出给调用者
    //把创建的异常类对象抛出给调用者
    throw new RuntimeException("参数不能为空");
    

异常处理有两种:声明、捕获,在程序开发中到底怎么选择使用哪个呢?

  • 自定义方法(程序员自己写的方法),通常都可以使用:声明

    • 方法体内代码比较清爽(阅读性好)
    • 把异常统一抛出到main方法中,进行统一的处理
  • 捕获的使用场景:

    1. main方法中只能使用捕获

    2. 父类型中的方法不支持throws,在子类重写方法时,重写的方法只能使用:捕获

      public class Demo  extends Thread{
          //重写方法
          public void run(){
      
              try {
                  Thread.sleep(100);
              } catch (InterruptedException e) {
                  e.printStackTrace();
              }
          }
      }
      

Throwable类 //异常的顶层父类型

  • 子类:Error类(错误类) //异常处理无法解决错误

  • 子类:Exception类(异常类)//可以使用异常处理解决(保证程序运行过程中不会中断)

编译时异常:Exception类

运行时异常:RuntimeException类 (继承了Exception)

自定义异常:

  • 程序员自己编写的异常类

  • 解决问题: JDK提供的异常类在命名上做不到见名其知意,通常在开发中程序员会自定义自己的异常类(见名其知意)

  • 使用:

    public 自定义异常类 extends Exception{ //当前自定义异常类为:编译时异常
        public 自定义异常类(){
            //super();//调用父类中的无参构造方法
        }
        public 自定义异常类(String message){
            super(message);
        }
    }
    
    public 自定义异常类 extends RuntimeException{ //当前自定义异常类为:运行时异常
        
    }
    

上午内容:

  • 异常

    //什么是异常
    程序在运行过程中发生了一些不正常情况,造成程序运行的中断
    
    
    //异常处理解决程序中的什么问题
    当程序中发生异常后,确保程序不能中断(跳过异常部分的代码,执行后续的代码)
    
    
    //异常处理怎么使用
    1、声明:throws       自己不处理交给他人处理
    2、捕获:try...catch  自己处理   
    
        
    public void method() throws 异常类 {
        //有编译时异常:throws、try...catch
    }    
    //默认的异常处理方式:向上抛出
    public void show() {
        //有运行时异常: try...catch | throws
    }
    
    publi void hello(){
        try{
            
            //可能会发生异常的代码
            
            
        }catch(Exception e){
            //处理异常
            //日志记录异常信息
        }
    }
        
        
    
    异常在使用中的注意细节
    try{
        
    }catch(异常子类 e){
        
    }catch(异常子类 e){
        
    }...
     catch(异常父类 e){
         
    } 
    
    main方法只能使用:try..catch
        
    
    //自定义异常
    public class 自定义异常类 extends RuntimeException{
        public 自定义异常类(){}
        public 自定义异常类(String message){
            super(message);
        }
    }   
    
    if( 参数 == null){
        //手动引发异常,并抛出
        throw new 自定义异常类("异常消息");
    }
    

Lambda

lambda作用:

  • 简化程序中的匿名内部类代码书写

lambda表达式:

  • 前置要求:仅针对函数式接口进行代码编写

    • 函数式接口的特点:接口中仅有一个抽象方法(允许有:静态方法、默认方法、私有方法)
    @FunctionalInterface    //java针对函数式接口,制定了一个注解:@FunctionalInterface
    public interface Comparator<T> {
        .......
    }
    
  • 标准语法:

    (参数 , ....) -> {
        //方法体代码(要做什么事情)
    }
    
    • () : 代表的是一个方法
    • -> : 指向要做的事情
    • { } : 功能代码(具体要做事情的代码)

Lambda表达式的省略模式:

  1. 可以省略参数类型:要么全部省略,要么全部保留
  2. 如果参数仅有一个时,可以省略小括号
  3. 如果代码块中仅有一行代码,可以省略:大括号、分号、return

Stream流

stream流的作用:

  • 针对集合进行功能简化开发

Stream流的使用通常需要Lambda表达式

Stream流方法分类:

  1. 获取方法:获取流(创建一个流水线)
  2. 中间方法:在流水线进行操作(例:过滤、截取)
  3. 终结方法:流水线上的操作结束了,要关闭流水线

获取Stream流对象:

//单列集合:Collection[ List、Set ]
Stream 流对象 = 单列集合对象.stream();


//双列集合:Map (不能直接获取流对象)
1. 先通过keySet()entrySet(),获取到Set集合
2. Stream  流对象 = Set集合对象.stream();    


//数组
Stream 流对象 = Arrays.stream( 数组 );

//特殊的操作: Stream流中的静态方法:  static<T> Stream<T>  of( T... args )
Stream 流对象 = Stream.of( 同一种类型元素 ,同一种类型元素,同一种类型元素, ... );

IO流中的必须掌握的内容:

  1. Properties

  2. 基础流(字节流、字符流)

  3. 概念:缓冲区(缓冲流的思想)

  4. 编码表(在开发过程中,乱码经常出现) //编码和解码保证使用相同的编码表

    • 编码

      String str = "黑马";
      byte[] buf = str.getBytes();
      
    • 解码

      byte[] buf = {-21, -13, -38};
      String str = new String(buf);
      
      String str = new String(buf , "编码表名字");
      
  5. 使用转换流读写特定编码的文件

  6. 序列化流(概念:序列化)

复习:线程通讯

  • 线程通讯:在A线程运行的过程中,可以去叫醒其他处于"等待状态"的线程

  • 线程通讯,需要使用到:等待唤醒机制

    • 等待:wait()
    • 唤醒:notify()、notifyAll()
    同步代码块(对象锁){
        对象锁.wait();
        
        
        对象锁.notify();//唤醒和当前对象锁绑定的其他处于等待状态的线程
    }
    
    
    非静态的同步方法(){ //对象锁:this
        this.wait();
        
        
        this.notify();
    }
    

复习:IO流

  • IO流的作用:对磁盘上的文件进行读、写操作
    • 读:把文件中的数据,读取到内存中
    • 写:把内存中的数据,写入到文件中(持久化存储)
  • IO流分类:
    • 字节流
      • 字节输入流:读 //父类:InputStream 常用子类:FileInputStream
      • 字节输出流:写 //父类:OutputStream 常用子类:FileOutputStream
    • 字符流
      • 字符输入流:读
      • 字符输出流:写
  • IO流使用套路:
    1. 创建:创建流对象
    2. 操作:读、写
      • 读:read()
      • 写:write()
    3. 关闭:释放流资源
      • close()

文件复制:

  • 方案1:循环一次读写1个字节数据 (程序性能低)
  • 方案2:循环一次读写多个字节数据(性能高)

读数据的方法:

//一次读取1个字节数据,返回实际读取到的字节数据;读取到文件末尾时返回:-1
int  read()  
    
    
//一次最多读取buf.length个字节数据,把读取到的字节数据存储到buf数组中,并返回实际读取字节数据的个数
//读取到文件末尾时返回:-1
int  read(byte[] buf)    


在IO体系下,java提供了高效流(提高读写文件的效率)

  • 读文件:BufferedInputStream
  • 写文件:BufferedOutputStream

在IO流中基础流只有两个:字节流、字符流

BufferedInputStream流的创建: 读数据

  • 自己没有读数据的能力,需要依赖字节输入流实现读数据
//构造方法: public  BufferedInputStream( InputStream  is )

BufferedInputStream bis = new BufferedInputStream( new FileInputStream("关联文件") );

BufferedOutputStream流的创建: 写数据

  • 自己没有写数据的能力,需要依赖字节输出流实现读数据
//构造方法: public  BufferedOutputStream( OutputStream  os )

//覆盖写入
BufferedOutputStream bos = new BufferedOutputStream( new FileOutputStream("关联文件") );


//追加写入 (构造方法的第二个参数为true)
FileOutputStream fos = new FileOutputStream("关联文件" , true); //追加写入
BufferedOutputStream bos = new BufferedOutputStream(  fos );//当前流有具有追加写入能力

Properties

Properties类:

  1. 作为Map集合的子类存在(存储的数据:key、value)
  2. 可以结合IO流使用 (读、写)

Properties类的基本使用:

  1. 作为集合使用
  2. 结合IO流使用

构造方法:

Properties prop = new Properties();

常用方法:

//向Properties集合中存储:key、value
setPorperty(String key , String value)

//根据指定的key,获取Properties集合中对应的value    
String getProperty(String key)
 
//获取Properties集合中所有的key元素    
Set<String>  stringPropertyNames();   

//借用字节输入流,读取配置文件,读取配置文件中的数据会存储到Properties集合中
load(InputStream  is )

在开发中,通常会使用到配置文件,而配置文件格式通常分为:

  1. XML文件
  2. properties文件

Properties类的作用:

  • 读取开发中使用的到.properties配置文件
    • properties配置文件中的数据是以key/value形式体现
    • 把properties配置文件中的key,存储到Properties类中的key元素下
    • 把properties配置文件中的value,存储到Properties类中的value元素下

ResourceBundle工具类

  • 作用: 读取项目工程下src目录中的.properties配置文件
//获取ResourceBundle对象 (必须保证properties配置文件存放在src目录下)
ResourceBundle rb = ResourceBundle.getBundle("properites配置文件名[不包含后缀名]")
 
//根据给定的key,获取value    
String value = rb.getString("key")     

递归

什么是递归?

  • 递归是应用于方法上的解决方案:方法自己调用自己

递归 = 递进 + 回归

  • 递进 :前进
  • 回归: 后退

递归的弊端:

  • 当递归调用的方法过多时,会造成栈内存的溢出

  • 解决方案: 队列结构

    1. 获取所有的File对象,存储到集合中
    2. 遍历集合
       判断:是文件
            判断:是否为java文件
       判断:是目录
            存储到集合中   
      
       细节: 从集合中获取一个数据后,该数据在集合中就无用了   
    

回顾上午内容:

//读写字节数组
int read(byte[] buf); //最多读取buf.length个长度的字节数据,把读取的字节数据存储到buf数组中
                      //返回实际读取到字节数据的个数,读取到文件末尾返回:-1

//把buf数组中从index开始向后取len个字节数据写入到文件中
void write(byte[] buf , int index , int len)
   
    
    
//字节缓冲流(高效流)   本身不具备读写能力,需要依赖字节流
//读数据:BufferedInputStream
//写数据:BufferedOutputStream    
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("关联文件"))
    
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("关联文件"))    
    
    

//读取properties配置文件
方式1Properties类
方式2ResourceBundleProperties prop = new Properties();
prop.load(new FileInputStream("xxxx.properties"));
String value = prop.getProperty("key")
    
    
//xxx.properties必须存放在当前项目工程的src目录下
ResourceBundle rb = ResourceBundle.getBundle("xxxx");//不需要指定后缀名
String vaule = rb.getString("key");

字符流

为什么使用字符流?

  • 针对纯文本文件中的中文数据,进行读写操作

为什么字节流读取中文数据时有:乱码?

  • 答案:编码表

编码表

概念:

  • 编码
    • 把看得懂的数据,变为看不懂的数据
  • 解码
    • 把看不懂的数据,变为看得懂的数据

在进行编码、解码时必须保证使用相同的编码表。

如果编码表不同,会出现:乱码

编码表:

  • 把生活中的数据和计算机中的数据整合在一起,存放在一张表格中
生活中的数据编码值计算机中的数据
A6501010101001010101
04801010101001011101
a9701010101001010111

字符流: 以字符为单位对文件进行读写操作 //读写都是做为字符数据

  • 字符输入流:读
  • 字符输出流:写

字符流可以读取中文的原因:

  • 字符流 = 字节流 + 编码表
    • 底层还是使用字节流读写数据,但是由于指定编码表,那么就可以一次读写2或3个字节数据

字符输出流:java.io.Writer类

  • Writer类是一个抽象类(不能实例化)
  • 常用子类:FileWriter
//创建字符输出流
Writer w = new FileWriter("关联文件"); //覆盖写入
Writer w = new FileWriter("关联文件" , true ); //追加写入

字符流 = 字节流 + 编码表

  • 内置:缓冲区(数组)
字符输出流对象.flush();//刷新流(把缓冲区中的数据写入到文件) 

字符输入流:java.io.Reader类

  • 是一个抽象类
  • 常用子类:FileReader
Reader r = new FileReader("关联文件");
//一次读取1个字符数据
int data = r.read();  //data中存储的是字符数据的编码值

//一次读取多个字符数据
int len = r.read( char[]  cbuf )//一次最多读取cbuf.length个字符数据,并把读取的字符数据存储到cbuf数组中。  返回实际读取到的字符数据个数。 读取到文件末尾返回:-1

缓冲流(高效流):

  • 字节缓冲流
    • BufferedInputStream:读
    • BufferedOutputStream:写
  • 字符缓冲流(本身不具备读写能力,需要依赖基本的字符流)
    • BufferedReader:读
    • BufferedWriter:写
//字符输入缓冲流
BufferedReader br = new BufferedReader(new FileReader("关联文件"));

//字符输出缓冲流
BufferedWriter bw = new BufferWriter(new FileWriter("关联文件"));//覆盖写入
BufferedWriter bw = new BufferWriter(new FileWriter("关联文件",true));//追加写入

字符缓冲流,提供了特有方法:

BufferedReader类中特有方法:
String readLine();//一次读取一行数据(以"\r\n"结尾) ,读取到文件末尾时返回:null

BufferedWriter类中特有方法:
void  newLine();//写入换行符(跨平台)    

转换流

转换流的作用:读写特定编码表的文件

  • FileReader类默认是UTF-8编码表(无法读GBK编码表的文件)

输入流:InputStreamReader

  • 自身没有读数据的能力,需要依赖字节输入流

输出流:OutputStreamWriter

  • 自身没有写数据的能力,需要依赖字节输出流
//InputStreamReader(InputStream 字节输入流, String 编码表名字) 
//创建输入流,并指定关联文件使用的编码表
InputStreamReader isr = new InputStreamReader(new FileInputStream("关联文件"),"GBK");



//OutputStreamWriter(OutputStream 字节输出流, String 编码表名字)
//创建输出流, 并指定关联文件使用的编码表
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("文件"),"GBK");

字符流的工作原理:

  • 利用了转换流实现:字节流和字符流之间的转换

Properties类

  • 继承自Map接口(Key、Value)

    • 可以使用Map集合中提供的功能

      put(Object key , Object value)
      remove(Object key)
      get(Object key)    
      
  • 可以结合IO流使用

  • 特有功能:

    //Properties类不需要泛型,存储都是String类型
    setProperty(String key , String value)
        
    String value = getProperty(String key)  
        
    Set<String>  stringPropertyNames()    
    

IO流

  • 固定套路:

    1. 创建IO流对象
        
    2. 读写文件
        
    3. 关闭流    
    
  • 固定的API方法:

    //读:read()
    1.读一个字节/字符
    int  read()    
    2.读一个字节/字符数组    
    int  read(byte[] buf)
    int  read(char[] cbuf)    
    
    
    //写:write()
    1.写一个字节/字符
    write(int data)    
    2.写一个字节/字符数组 
    write(byte[] buf , int index , int len)
    write(char[] buf , int index , int len)    
    3.写字符串(针对字符流) 
    write(String s)    
        
    //关闭流
    close()    
        
    //针对字符流、缓冲流
    flush()     
    
  • 基础流

    //字节流 : 读写任意类型的文件
    FileInputStream fis = new FileInputStream(关联文件对象)
    FileOutputStream fos = new FileOutputStream("关联文件")    
    FileOutputStream fos = new FileOutputStream("关联文件", true)//追加写入
        
    //字符流 : 针对纯文本文件进行读写
    FileReader  fr = new FileReader("关联文件")
    FileWriter fw  = new FileWriter("关联文件")    
    FileWriter fw  = new FileWriter("关联文件",true) //追加写入       
    
  • 缓冲流(高效流):自身不具备读写能力,要依赖基础流

    //特点:提高读写效率
    
    //字节缓冲流
    BufferedInputStream bis = new BufferedInputStream( FileInputStream对象 )
    BufferedOutputStream bos = new BufferedOutputStream( FileOutputStream对象 )   
    
    
    //字符缓冲流
    BufferedReader br = new BufferedReader( FileReader对象 ) 
    BufferedWriter bw = new BufferedWriter( FileWriter对象 )  
    特有功能:
        String readLine()
        void newLine()
    
  • 转换流:自身不具备读写能力,要依赖基础流

    //特点:针对特有编码表的文件进行读写
    InputStreamReader //读
    OutputStreamWriter //写
    

不清楚要关闭哪些资源:

try(
    //创建IO流对象
){
    //IO操作
    
}catch(Exception e){
    
}

实例对象:

//在实现父接口时,直接指定了接口上使用的泛型类型
public class 子类 implements Callable<Integer>{
    //重写抽象方法
    
}
Callable  call = new 子类();


//不想在创建一个.java文件,编写子类了
new Callable(){
    //重写抽象方法
}

泛型类的定义:

public class 类名<E>{
    
}
public class 子类<E> implements Callalbe<E>{
    //子类在实现接口时,没有指定接口上的泛型类型
    //子类继续延用接口上的泛型(子类变为:泛型类)
}

Properties:读配置文件

//创建Properties对象
Properties prop = new Properties();

//加载配置文件,并把配置文件中的内容,填充到Properties对象中
prop.load( new FileReader("xxxx.properties") )//读带有中文的配置内容

//从Properties对象中获取value值
String value = prop.getProperty("key")    

对象操作流

对象操作流,还称为:序列化流

对象操作流的应用:

  • 把程序中创建的对象,先写入到文件中(持久化存储)
  • 即使重启计算机后
  • 通过程序读取文件中存储的对象,可以重新把对象加载到内存中

对象操作输入流: 从文件中读取存储的对象

  • IO流类:ObjectInputStream

    • Object readObject()
      

对象操作输出流: 把内存中创建的对象,写入到文件中

  • IO流类:ObjectOutputStream

    • void writeObject(Object obj)
      

序列化流:把对象序列化后写入到文件或在网络中传输

反序列化流:将文件中存储对象(序列化后)读取到内存中或接收网络中传输的对象

问题:使用序列化流向文件中写入多个User对象,怎么解决?

答案:把多个User对象存储到集合中,使用序列化流向文件中写入集合对象

在Java语言中存在一种特殊的接口:标记接口(空接口)

  • 仅有接口的定义,接口内没有任意内容
  • 作用:给类的实例添加一个标记(serialVersionUID)

异常:InvalidClassException

com.itheima.objectstream.User; 
local class incompatible: 
stream classdesc serialVersionUID = 5595734625571666011,  //流中的User类版本号(串行版本)
local class serialVersionUID = -8394195331070918768       //本地User类版本号(串行版本)
    
//类的串行版本与从流中读取的类的不匹配     

序列化操作的细节:

  • 当某个成员变量,不希望进行序列化操作时(成员变量以及数据不写入到文件中),可以使用transient关键字修饰成员变量

打印流(了解)

  • 作用:

    1. 在写入数据后可以实现自动换行
    2. 通常用于日志记录
  • 类:PrintStream

    public PrintStream(String filePath)
    
  • 常用方法:

    public void println(数据)   打印后换行
    public void print(数据)     打印不换行
    

装饰者设计模式(代码简单、知道解决哪类问题)

Common-io(工具类,简化IO操作的,用就行了)

设计模式:

  • 认知:设计模式就总结出来的一套解决开发中各类问题现成方案(固定套路)
  • 已学习的设计模式:模板设计模式

装饰者设计模式:

  • 不改变原类, 不使用继承的基础上,动态地扩展一个对象的功能(功能增强)

装饰者模式的套路:

  1. 装饰类和被装饰类需要有共同的父类型
  2. 装饰类的构造要接收被装饰类的对象
  3. 在装饰类中把要增强扩展的功能进行扩展
  4. 对于不要增强的功能直接调用
//字符输入缓冲流: BufferedReader      特点:提高读的效率

情况:在使用FileReader类时,发现读的效率低,希望提高效率

//不能修改FileReader类、不能继承FileReader类 ,要对read(char[] cbuf)方法进行增强

//使用装饰者模式解决:
    装饰类:  BufferedReader
    被装饰类: FileReader
    
//装饰类      具有相同的父类型
public class BufferedReader extends Reader{
    //被装饰类
    private FileReader fileReader;
    
    //构造方法
    public BufferedReader(){}
    public BufferedReader(FileReader fileReader){//接收被装饰类的对象
        this.fileReader = fileReader;
    }
    public BufferedReader(FileReader fileReader , int size){
        this.fileReader = fileReader;
        this.size = size;
    }
    
    //成员变量
    private int size = 1024*8;
    
    //缓冲区对象
    char[] cbuf = new char[size];
    
    //重写read()方法
    public int read(){
        ....
            
        read(cbuf);
        
    }
    //重写read(char[] cbuf)方法
    public int read(char[] cbuf){
        .......
    }
    
    
    
    
    //重写相关方法
    public void close(){
        fileReader.close();//调用原有功能
    }
}    
//多态
Reader  read = new BufferedReader( new FileReader("关联文件") );


IO流:

  • 基础流
  • 序列化 / 反序列化

有第三方组织针对IO中大量的API进行简化,只提供一些简单API方法,就可以实现IO读写操作

  • 工具包:Commons-io

通常java程序开发完成之后,要进行打包:把程序压缩为一个文件

  • jar包:普通的java程序
  • war包: java web程序

IOUtils类:

  • 针对IO流进行读写操作(单文件vs单文件)
public static int copy(InputStream in, OutputStream out):
	把input输入流中的内容拷贝到output输出流中,返回拷贝的字节个数(适合文件大小为2GB以下)
    
public static long copyLarge(InputStream in, OutputStream out):
	把input输入流中的内容拷贝到output输出流中,返回拷贝的字节个数(适合文件大小为2GB以上)

FileUtils类:

  • 针对File对象进行读写操作(单文件vs单文件 、 目录vs目录)
public static void copyFileToDirectory(final File srcFile, final File destFile): 
	复制文件到另外一个目录下。
public static void copyDirectoryToDirectory(File src , File dest ):
	复制src目录到dest位置。

Java基础:

  • 语法
  • 面向对象

Java的应用:

  • 基础API:字符串、日期、…

  • 集合

  • 多线程

  • IO流

  • 网络编程

集合+多线程+IO流+网络编程+设计模式+反射+注解 = 所有技术框架

  • 学习的软件:Tomcat(底层:网络编程+多线程+IO流)
  • 框架技术: 设计模式+反射+注解+集合+多线程

在企业开发中,程序存在两种结构模型:

  • C / S : 客户端/服务端
    • 需要开发客户端和服务端,两端程序(开发成本高)
  • B / S : 浏览器/服务端
    • 浏览器:是由第三方浏览器厂商已提供了(不需要开发了)
    • 服务端:需要开发程序

在网络中想要实现编程,必须有:网络三要素

  • IP地址:网络上计算机的唯一标识
  • 端口号:计算机中程序的唯一标识(每一个程序都有自己唯一的端口号)
  • 通信协议:网络上程序之间交互的规则
    • TCP协议
    • UDP协议

老唐的IP:192.168.32.61

本地回环地址(本机):127.0.0.1

网络编程:会使用到java.net包下的AIP

  • java.net.InetAddress类 : IP地址

    //获取InetAddress对象
    InetAddress ipObj = InetAddress.getByName("计算机名 / IP地址")
    
    //调用方法
    String hostname = ipObj.getHostName();
    String ip = ipObj.getHostAddress();
    

常见通信协议 :

  • UDP协议
    • 特点:
      1. 面向无连接协议
      2. 数据大于限制在64K以内
      3. 数据不安全,容易丢失
      4. 传输速度快
  • TCP协议:传输控制协议
    • 特点:
      1. 面向有连接协议(利用TCP三次握手机制)
      2. 数据大小没有限制
      3. 数据相对安全(相对UDP协议来讲)
      4. 传输速度慢

java编程中使用的包:

java.lang

java.util.集合

java.text.SimpleDateFormat

java.io

java.net

java.sql


javax //扩展包

TCP通信程序,要求必须有两端程序:

  • 客户端/浏览器
  • 服务端

在Java语言中,想要实现客户端、服务端两个程序之间进行交互,需要依赖:Socket (利用TCP协议)

  • 客户端程序:Socket
    • 先找到计算机 //IP地址
    • 再找到计算机上的服务端程序 //端口号
    • 按照TCP协议进行数据发送、接收
      • 是基于IO流实现数据的发送、接收
  • 服务端程序:ServerSocket
    • 绑定服务端程序的端口号

java.net.Socket类:客户端

public Socket(String address , int port)
//参数1: ip地址或主机名
//参数2: 端口号(服务端程序的端口号)

OutputStream  getOutputStream()  //基于socket对象获取网络输出流(发送数据)
InputStream  getInputStream()  //基于socket对象获取网络输入流(接收数据)
//1. 创建客户端Socket对象,并连接服务端程序
Socket socket = new Socket("对方计算机IP地址", "服务端程序端口号");
//2. 基于socket对象,获取网络输出流(发送数据)
OutputStream netOut = socket.getOutputStream();
//3. 发送数据
netOut.write("数据".getBytes());

//4. 关闭资源
netOut.close();
socket.close();

java.net.ServerSocket:服务端

public ServerSocket(int port)
//参数:port 当前服务端程序要绑定的端口号(客户端Socket程序要使用这个端口号和服务端连接) 
    
    
public Socket accept();//监听客户端连接,并生成连接,返回一个Socket对象    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值