仓库

目录

一、Java语言特点

  1. 面向对象
  2. 平台无关
  3. 健壮
Java两种核心机制
Java虚拟机

.java源文件通过编译形成 .class字节码文件,再通过在不同平台上的JVM执行字节码文件

  1. Java虚拟机可以理解成一个以字节码为机器指令的CPU;
  2. 对于不同的运行平台有不同的虚拟机实现;
  3. Java虚拟机机制屏蔽了底层运行平台的差异,实现平台无关性。
垃圾收集
  1. 不再使用的内存空间应回收;
  2. Java消除了程序员回收无用内存空间的责任,提供一种系统级现场跟踪存储空间的分配情况,并在jvm空闲时,检查并释放哪些可被释放的存储空间;
  3. 垃圾收集在Java程序运行过程中自动进行,程序员无法精确控制和干预;

JDK:development kit(开发包)

JRE:runtime environment(运行环境)

二、语法

标识符
  • Java对各种变量、类、方法等要素命名时使用的字符序列;
  • 规则:
    1. 英文字母、数字、_或$组成;
    2. 数字不可开头;
    3. 不可使用关键字或保留字;
    4. 区分大小写;
    5. 不包含空格;
Java中名称命名规范

包名:字母都小写;

类名、接口名:多单词组成,首字母大写;

变量名、方法名:首字母小写,驼峰命名;

常量名:多单词下划线链接;

关键字、保留字
局部变量、成员变量
  • 变量作用域
  • 内存布局
基础类型变量(4类8种)
byte  short  int(默认)  long(加L) char  boolean  float(加f)  double(默认)
8 bit   16    32            64     16      1         32           64
基本类型的转换
  1. 自动类型转换:小的自动转大的。
  2. 有多种类型的数据混合运算时,系统首先自动将所有数据转换成容量大的那种数据类型再进行计算。
  3. 数字类型的运算中,多个相同类型变量参与的运算,变量要先转换为相对应的数据的默认类型(比如两个byte的变量相加,会先把两个byte类型的变量转换成默认的int类型之后再计算,得到的结果时int类型)
  4. byte、short、char之间不会相互转换,他们三者在计算时首先转换成int类型。
  5. 当把任何基本类型的值和字符串值进行连接运算时(+),基本类型的值将自动转化为字符串类型。
强制转换
  1. 自动转换的逆过程;

    short short1= (short) aLong;
    int int1= (int) aLong;
    
  2. 字符串通常不能直接转换为基本类型,但通过基本类型对应的包装类可以实现字符串转成基本类型(Integer.parseInt(“123”,Integer.valueOf(s),valueOf() 方法里先调用parseInt()));

  3. boolean类型不能转成其他类型;

数组
//静态初始化
int arr2[]=new int[]{1,2,3,4,4,2};
int[] arr3={1,2,3,4,4,2};
//动态初始化
int[] arr1=new int[10];
arr1[0]=1;
//静态初始化
int[][] arr4=new int[1][4];
//动态初始化
int[][] arr5=new int[3][];
  • 数组元素的引用
    1. 定义并用运算符new分配空间后才可以引用数组中的每个元素;
    2. 数组元素的引用方式:数组名[元素下标];
    3. 数组元素长度:arr.length;一旦初始化,长度不可变;
  • 数组元素的默认初始化
    • 数组是引用类型,它的元素相当于类的成员变量,数组分配空间后会被隐式初始化,默认为0.
  • 数组中涉及的常见算法
    1. 最大、最小、平均、总计

    2. 复制

      //1 clone()
      int[] a1=arr.clone();
      
      //2 System.arraycopy(原数组, 原数组的开始位置, 目标数组, 目标数组的开始位置, 拷贝个数)
      int[] a2=new int[10];
      System.arraycopy(arr, 0, a2, 0, 3);
      
      //3 Arrays.copyOf(原数组,拷贝的个数)
      //Arrays.copyOf底层其实也是用的System.arraycopy
      int[] a3=Arrays.copyOf(arr,10);
      
      //4 Arrays.copyOfRange(原数组,开始位置,拷贝的个数)
      //Arrays.copyOfRange底层其实也是用的System.arraycopy,只不过封装了一个方法
      int[] a4=Arrays.copyOfRange(arr,0,arr.length);
      
    3. 排序

      Arrays.sort(arr);
      
  • 数组操作常见问题
    1. 下标越界(ArrayIndexOutOfBoundsException)
    2. 空指针异常(NullPointerException)
引用类型

类、接口、数组;

数据类型之间的转换

如果低级类型为char型,向高级类型(整型)转换时,会转换为对应ASCII码值

运算符
  1. 算术运算符 +,-,++,–,*,/,%,

  2. 赋值运算符 =,+=,-=,*=,/=,%=

  3. 比较运算符 ==.!=.<,>,>=,<=

  4. 逻辑运算符 & && | || ! ^

  5. 位运算符 >> << >>> & ~ | ^

    "<<"左移 m<<n => m*2^n

    ">>"右移 m>>n => m*2^-n

    “>>>” 无符号右移

    “&”与 6&3=2

    “|”或 6|3=7

    “^”异或 6^3=5

    "~"反码 ~6=-7

  6. 三元运算符

运算符的优先级
  • [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NDYjROF6-1579225422411)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200106172403557.png)]

    程序流程控制
    • 顺序

      Java中定义成员变量时采用合法的前向引用;

    • 分支

      if-else和switch两种;

    • 循环

      while、do-while、for三种。jdk1.5后还有foreach循环。

    方法
    • 形参、实参、返回值、返回值类型
    • 递归调用

三、面向对象

面向过程和面向对象
  • 面向对象的基本思想是,从现实世界中客观存在的事物出发构建软件系统,并在系统的构建中,尽量用人的思维。

  • 面向过程强调的是功能行为;面向对象强调具备了功能的对象;

  • 面向对象的三大特性

    1. 封装
    2. 继承
    3. 多态
对象和类的概念

对象时计算机语言对问题域中的十五的描述,“属性”和“方法”对于事物的静态属性和动态属性;

类是用于描述同一类型对象的一个抽象概念,类中定义了这类对象的静态属性和动态属性;

类是一类对象的模板,对象可以看成该类的一个具体实例;

类之间的关系
  1. 关联关系:对象之间一种引用关系,通常使用类的属性表达;单双向实线
  2. 依赖关系:最弱的一种关联方式,是临时性的关联;由局部变量、函数参数、返回值建立的对于其他对象的调用关系;虚线箭头
  3. 聚合关系:(关联关系的一种)表示 has-a 的关系;整体与个体的关系;空心菱形
  4. 组合关系:组合也是关联关系的一种特例,它体现的是一种contains-a的关系,这种关系比聚合更强,也称为强聚合。它同样体现整体与部分间的关系,但此时整体与部分是不可分的,整体的生命周期结束也就意味着部分的生命周期结束;实心菱形
  5. 继承关系:泛化, is-a 的关系;类与类的继承关系,类与接口的实现关系;空三角箭头实线
  6. 实现关系:实现指的是一个class类实现interface接口;空三角箭头虚线
为什么用对象?
  1. 面向对象的编程,对象更加符合对于现实问题的抽象;
  2. 对象都有对外服务的接口;
  3. 对象隐藏内部服务的实现;
  4. 面向对象更加容易是我们达到可重用、易扩展、方便维护和替换的境界;
成员变量
  1. 成员变量可以是基本类型或者引用类型;
  2. 成员变量默认初始化;
  3. 作用范围为整个类体;
对象和引用

除了基本类型之外的变量类型都是引用类型。Java中的对象都是通过引用对其操作的。

构造函数
  1. 隐式午餐构造器;
  2. 显式定义一个或多个构造器;

注意:

  • 每个类至少有一个构造器;
  • 默认构造器的修饰符和所属类的修饰符一致;
  • 一旦显式定义了构造器,系统不再提供默认构造器;
  • 一个类可以创建多个重载的构造器;
  • 父类的构造器不可被子类继承;
对象的创建和使用
  1. 定义类(考虑修饰符、类名);
  2. 编写类的属性(修饰符、属性类型、属性名、初始化值);
  3. 编写类的方法(修饰符、返回值类型、方法名、形参);
修饰符

private<缺省<protect<public

  • class的权限修饰只可以用public和缺省;
  • public类可以在任意地方被访问;
  • default类只可以被同一个包内部的类访问;
成员变量和局部变量的区别
  1. 成员变量
    • 定义在类中,整个类中都可以被访问;
    • 分为类成员变量和实例成员变量;
    • 实例变量存在对象所在的堆内存中;
    • 有默认初始化值;
    • 可选权限修饰符;
  2. 局部变量
    • 定义在局部范围内;
    • 存在栈中;
    • 作用的范围结束,变量空间自动释放;
    • 必须显式初始化;
    • 不指定权限修饰符;
匿名对象
  • ·不定义对象的句柄,直接调用该对象的方法。

    System.out.println(new Teacher().showInfo());
    
面向对象思想“落地”法则(一)
  1. 关注类的设计;
  2. 类的实例化;
  3. 通过“对象.方法”,“对象.属性”执行;
方法的重载(overload)
  • 同一个类中,允许存在一个以上的同名方法,只要它们的参数个数或者参数类型不同或顺序不同即可。

  • 与返回值类型无关,只看参数列表,且参数列表必须不同;

    //函数重载
    public int reload(){
        System.out.println("reload 1");
        return 0;
    }
    public void reload(int x,float f){
        System.out.println("reload 2");
    }
    public void reload(float f,int x){
        System.out.println("reload 3");
    }
    
  • 方法的可变个数形参
    1. 可变参数:方法参数部分指定类型的参数个数是可变个数;

    2. 声明方式:方法名(参数类型名…参数名);

    3. 可变参数方法的使用和方法参数部分使用数组是一致的;

    4. 方法的参数部分有可变形参,需要放在形参声明的最后;

      /**
       * 可变个数形参,与数组的使用方式相同
       */
      public void printInfo(int i,String...args){
          for (String s:arg){
              System.out.println(s);
          }
      }
      
  • 方法的参数传递
    1. 方法必须有其所在的类或对象调用才有意义。若方法含有参数;
      • 形参:方法声明时的参数;
      • 实参:方法调用时传给形参的参数值;
    2. 实参如何传入方法
      • 值传递;
package关键字
  • 顶层包名.子包名;
  • 通常用小写单词,类名首字母通常大写;
import 语句
  • java.lang包不需要显式声明,编译器默认可获取;
  • import ee.*;表明导入ee包下的所有类,而子包中的类不会被导入;
  • import 语句不是必须的,可在类中使用其他类的全名;
  • jck1.5加入import static语句;
JDK中主要的包
  • java.lang
  • java.net
  • java.io
  • java.util
  • java.text
  • java.sql
  • java.awt
  • java.applet
封装和隐藏
  • 使用者对类内部定义的属性(对象的成员变量)的直接操作会导致数据的错误、混乱或安全性问题。
this关键字
  • this不可用在static方法中;
  • 使用this()必须放在构造器的首行;
  • 使用this调用本类中其他的构造器,保证至少有一个构造器是不用this;
JavaBean
  • Java语言写成的可重用组件;
  • 符合如下标准的Java类
    1. 类是公共的;
    2. 有一个无参的公共的构造器;
    3. 有属性,且有对应的get\set方法;
static关键字
类的继承
  • 子类不能直接继承父类中的 private 属性和方法。

作用:

  1. 复用性;
  2. 类与类之间产生了关系,提供了多态的前提。
  3. 不要近未来获取其他类中某个功能而去继承;
方法的重写(override)

在子类中对从父类继承来的方法进行改造,也称方法的重置、覆盖;

要求:

  1. 方法名称、参数列表、返回值类型相同;
  2. 访问权限不能更严格;不可以降低方法的可访问性。
  3. 必须同时为static或者同时为非static;
  4. 子类方法抛出的异常不能大于父类被重写方法的异常;
super关键字
  • 可用于访问父类中的属性;
  • 可用于调用父类中的成员方法;
  • 可用于在子类构造方法中调用父类的构造器;

注意:

  1. 子父类出现同名成员可用super区分;
  2. super的追溯不仅限于直接父类;
  3. super与this用法相像,super代表父类的内存空间的标识;
this与super的区别:
  1. 访问属性:this访问本类中的属性,如果本类没有则从父类中继续查找;
  2. 调用方法:this访问本类方法;super直接访问父类中的方法;
  3. 调用构造器:调用本类构造器必须放在构造器首行;调用父类构造器必须放在子类构造器首行;在子类中,通过this或super调用构造器,只能使用一个this()与super()中的一个;
  4. 特殊:this表示当前对象;
简单类对象的实例化过程
  1. (方法区)加载Person.class;
  2. (栈内存)
    1. 在栈中申请空间,声明变量p;
    2. 构造函数进栈,进行初始化;
    3. 初始化完毕后,将堆内存中的地址赋值给引用变量,构造方法出栈;
  3. (堆内存)
    1. 在堆中开辟空间,分配地址;
    2. 在对象空间中,对对象中的属性进行默认初始化,类成员变量显式初始化;
子类对象的实例化过程

初始化顺序:默认初始化——>显式初始化—— >构造函数初始化(没父类)看起来实际构造器是先加载的,先会进行父类的的初始化,在super对父类进行初始化完毕后,才会进行子类的成员变量显式初始化。

  1. 先加载父类.class,后加载子类. class;
  2. 在栈中申请空间,声明变量someKid;
  3. 在堆内存中开辟空间,分配地址;
  4. 在对象空间中,对对象中的属性(包括父类的属性)进行默认初始化;
  5. 显式初始化父类的属性;
  6. 显式初始化子类的属性;
  7. 子类构造方法进栈;
  8. 父类构造方法进栈,执行完毕出栈;
  9. 子类构造方法出栈,初始化完毕后,将堆中的地址值赋给引用变量,子类构造方法出栈;
public class Children extends Father{
    private String hobby="篮球";//显式初始化在父类构造方法执行结束后进行
//    private Children children=new Children();//java.lang.StackOverflowError
    static {
        System.out.println("2-子类静态代码块");
    }
    {
        System.out.println("5-子类构造代码块");
    }
    Children(){
        System.out.println("6-子类构造方法");
    }
    public Children(String hobby) {
        this.hobby = hobby;
    }
    public void show(){
        System.out.println("7-子类show");
    }
    public static void main(String[] args) {
        Children children=new Children();
        children.show();
    }
}
class Father{
    int age=20;
    static {
        System.out.println("1-父类静态代码块");
    }
    {
        System.out.println("3-父类构造代码块");
    }
    Father(){
        System.out.println("4-父类构造方法");
    }
    public void show(){
        System.out.println("父类show");
    }
}
/*
1-父类静态代码块
2-子类静态代码块
3-父类构造代码块
4-父类构造方法
5-子类构造代码块
6-子类构造方法
7-子类show
* */

父类静态代码块 =》子类静态代码块 =》父类显式初始化(构造代码块)=》父类构造方法 =》子类显式初始化(构造代码块)=》子类构造方法

final关键字
多态

两种体现:

  1. 方法的重载和重写;
    • 重载:同名方法实现不同逻辑;
    • 重写:子类对父类方法的覆盖;
  2. 对象的多态性(抽象类和接口(继承或者实现关系));
    • 子类的对象可以替代父类的对象使用;
      • 一个变量只能由一种确定的数据类型;
      • 一个引用变量可能指向多种不同类型的对象;
    • 子类可看做是特殊的父类,所以父类类型的引用可以指向子类的对象(向上转型),但不能访问子类中添加的属性和方法了;

引用变量有两个类型:编译时类型和运行时类型。编译时类型有声明变量时使用的类型决定;运行时类型有实际赋给该变量的对象决定;由此不一致时就出现多态。

虚拟方法调用
  • 编译时类型和运行时类型
  • 编译时类型为Father类型,而方法的调用是在运行时确定的,所以调用的是Children类的个体show()方法。(动态绑定)
Father children=new Children();
children.show();
x instanceof A
  • 检验x是否为类A的对象,返回值是boolean;
  • 如果x是类A的子类B,x instanceof A 为true;
Object类
  • Object类是所有Java类的根父类。
  • hasCode()、toString()、equals()
对象转型
  • 基本数据类型
    • 自动转型
    • 强制转型
  • 对Java对象的强制类型转型成为造型
    1. 从子类到父类可以自动进行;
    2. 从父类到子类必须强制类型转换;
    3. 无继承关系的引用类型间的转换是非法的;
== 与equals
  • ==
    1. 基本类型比较值;两边数据类型必须兼容(可自动转换的基本数据类型除外)
    2. 引用类型比较是否指向同一个对象
  • equals
    1. 所有类都继承了Object,也就获得了equals()方法,还可重写;
    2. 只能比较引用类型(作用与==相同);
包装类
  • 基本数据类型包装成包装类的实例;
  • 获取包装类对象中的基本数据类型;
  • jdk1.5后支持自动装箱、自动拆箱;

字符串与基本数据类型

int i=new Integer("123");
int i1=Integer.valueOf("123");//本身调用了paseInt()方法
int i2=Integer.parseInt("123");

String s1=String.valueOf(2.34f);
static关键字

在Java类中,可用static修饰属性、方法、代码块、内部类;由该类所有对象共享;

static方法内部不能有this(或super);

被修饰后具备以下特点:

  1. 随类加载而加载;
  2. 优先与对象存在;
  3. 修饰的成员,被所有对象所共享;
  4. 访问权限允许时,可不创建对象,直接被类调用;
单例设计模式
/**
 * 饿汉式
 */
public class Singleton {
    private static Singleton instance=new Singleton();
    //private 只有内部可以实例化
    private Singleton(){}
    public static Singleton getInstance(){
        return instance;
    }
}

/**
 * 懒汉式 实例延迟加载
 */
class Singleton2{
    private static Singleton2 instance=null;
    private Singleton2(){}
    public static Singleton2 getInstance(){
        if(instance==null){
            instance=new Singleton2();
        }
        return instance;
    }
}
/**
 * 双重加锁机制
 */
class Singleton3{
    private static volatile Singleton3 instance=null;
    private Singleton3(){}
    public static Singleton3 getInstance(){
        if(instance==null){
            synchronized (Singleton3.class){
                if(instance==null) {
                    instance = new Singleton3();
                }
            }
        }
        return instance;
    }
}
/**
 *静态内部类
 */
class Singleton4 {
    private static class SingletonHoler {
        /**
         * 静态初始化器,由JVM来保证线程安全
         */
        private static Singleton4 instance = new Singleton4();
    }
    private Singleton4() {}
    public static Singleton4 getInstance() {
        return SingletonHoler.instance;
    }
}

我们 JDK 中,java.lang.Runtime 就是经典的单例模式(饿汉式);

破坏单例模式的三种方式:
  1. 反射
  2. 序列化
  3. 克隆

解决方案如下:
1、防止反射
定义一个全局变量,当第二次创建的时候抛出异常
2、防止克隆破坏
重写clone(),直接返回单例对象
3、防止序列化破坏
添加readResolve(),返回Object对象

main方法
  • Java运行的类名 String数组args保持执行Java命令时传递给运行的类的参数;
final关键字 (类、属性、方法)
  1. 标记的类不能被继承;
  2. 标记的方法不能被子类重写;
  3. 标记的变量(成员变量或局部变量)称为常量。必须在声明的同时显式赋值然后才能使用。只能赋值一次;
  4. 标记的是基本数据类型时值不可变,标记的是引用变量时所引用地址不可变;
初始化块
  1. 对Java对象进行初始化;

  2. 程序执行顺序:

    声明成员变量的默认值》显式初始化、初始化块》构造器再对成员赋值;

  • 静态代码块:初始化类的静态属性;

  • 非静态代码块

/**
 * 匿名内部类--没有类名的Person类的子类,也就是匿名的Person的子类;
 * 这种类没有类名,就不能显式的new的方法创建对象,如果要是还有在构造器中
 * 初始化属性就没有办法了,这样情况就要用代码{}初始化的工作;
 */
Person p=new Person(){
    {//用代码块代替构造方法,父类构造代码块-父类构造方法-子类构造代码块-子类构造方法
        super.setName("李四");
    }
    @Override
    public void showName() {
        super.showName();
    }
};
p.showName();
抽象类
  • 用abstract关键字修饰类为抽象类;

  • 用abstract关键字修饰方法为抽象方法;

  • 含有抽象方法的类必须声明为抽象类,抽象类可以没有抽象方法;

  • 抽象类不能被实例化,抽象类是用来被继承的;抽象类的子类必须重写父类的抽象方法,并提供方法体。若没有重写全部抽象方法,则仍为抽象类;

  • 不能用abstract修饰属性、私有方法、构造器、静态方法、final方法

抽象类是用来模型化那些父类无法确定全部实现,而是由其子类提供具体实现的对象的类;

public abstract class AbstratTest {
    private int anInt=0;
    public abstract void method1();
    public void method2(){
        System.out.println("1");
    }
}
class subTest extends AbstratTest{
    @Override
    public void method1() {
        System.out.println("实现了抽象方法");
    }
}
  1. 为什么抽象类不可以使用final关键字声明?

    抽象类不能实例化,是用来被继承的;

  2. 一个抽象类中可以定义构造器吗?

    抽象类可以有构造方法,只是不能直接创建抽象类的实例对象而已;

模板方法设计模式

抽象类体现的就是一种模板模式的设计,抽象类作为模板,子类在抽象类的基础上进行拓展、改造,但子类总体会保留抽象类的行为方式;

解决的问题:

  1. 当功能内部一部分实现是确定的,一部分实现是不确定的,这时可以把不确定的部分暴露出去,让子类去实现。
  2. 编写一个抽象父类,父类提供了多个子类的通用方法,并把一个或多个方法留给其子类实现,就是一种模板模式。
接口

有了接口可以得到多重继承的效果;

  • 接口是抽象方法和常量值的定义的集合;

  • 从本质上看,接口是一种特殊的抽象类,这种抽象类中只包含常量和方法的定义,而没有变量和方法的实现;

  • 实现接口类:

    class SubClass implements InterfaceA
    
  • 一个类可以实现多个接口,接口也可以继承其他接口;

接口的特点
  1. 用interface定义、
  2. 接口中的所有成员变量都默认是由public static final修饰的;
  3. 接口中的所有方法都默认是由public abstract 修饰的;
  4. 接口没有构造器;
  5. 接口采用多层继承机制;
public interface InterfaceA {
    int ID=0;//默认为 public static final 修饰
    void start();//默认为 public abstract 修饰
}
//只能继承一个类,可以implements实现多个接口,接口也可以extends继承其他接口;
//类定义 先写继承后写实现
public class SubClass extends classA implements InterfaceA,InterfaceB {
    public SubClass(int age) {
        super(age);
    }
    @Override
    public void start() {
        System.out.println("subClass start");//继承自InterfaceA
    }
    @Override
    public void show() {
        System.out.println("subClass show");//继承自InterfaceB
    }
    @Override
    public String getInfo() {
        return "subClass getInfo";//继承自InterfaceC
    }
}
interface InterfaceB extends InterfaceC{
    void show();
}
interface InterfaceC{
    String  getInfo();
}
class classA{
    public int age;
    public classA(int age) {
        this.age = age;
    }
}
  1. 实现接口的类中必须提供接口中所有方法的具体实现内容方可实例化。否则必须定义为抽象类
  2. 接口的主要用途就是被实现类实现(面向接口编程)
  3. 与继承关系类似,接口与实现类之间存在多态
工厂方法

FactoryMethod 模式是设计模式中应用最为广泛的模式;

它通过面向对象的手法,将所要创建的具体对象的创建工作延迟到了子类,从而提供了一种扩展的策略,较好地解决了这种紧耦合的关系;

interface Shape {
    void draw();
}
class Circle implements Shape{

    @Override
    public void draw() {
        System.out.println("Circle-draw");
    }
}
class ShapeFactory{
    static Shape getShape(String className){
        if(className==null){
            return null;
        }
        switch (className){
            case "Circle":
                return new Circle();
            default:
                return null;
        }
    }
}
内部类

参考blog

  • 一个类定义在另一个类的内部;前者称为内部类,后者称为外部类;
  • inner class一般用在定义它的类或语句块之内,在外部引用它时必须给出完整的名称;
  • 内部类可以使用外部类的私有数据,因为它是外部类的成员,同一个类的成员之间可互相访问。而外部类要访问内部类中的成员需要:内部类.成员或者内部类对象.成员

分类:

  • 成员内部类(static或非static);
  • 局部内部类(不谈修饰符);
  • 匿名内部类;
内部类特性
  1. 内部类作为类的成员

    • 可以声明为final;
    • 和外部类不同,可声明为private或protect;
    • 内部类可以声明为static,但此时就不能在使用外层类的非static的成员变量;
  2. 内部类作为类可以声明为abstract类,因此可以被其他的内部类继承;

  • 非static内部类中的成员不能声明为static的,只有在外部类或static内部类中才可声明static成员
public class ClassWithInnerClass {
    public static int aStaticInt;
    public int anInt=0;
    //普通内部类对象依赖外部类对象而存在,像外部类声明的一个属性字段一样
    public class InnerClass1{
        //非static内部类不可有static成员
        //public static int anInt=0;
        public int anInt=0;
        public String name;
        public void mb(){
            anInt=100;
            System.out.println("在内部类中 anInt="+anInt);
        }
        public InnerClass1(){
            System.out.println("InnerClass1 构造函数");
            anInt=InnerClass2.anInt;
        }
    }
    public static class InnerClass2{
        private static int anInt=2;
        private  String name;
        public InnerClass2(){
            System.out.println("InnerClass2 构造函数");
        }
    }
    public static void main(String[] args) {
        ClassWithInnerClass c1=new ClassWithInnerClass();
        InnerClass1 innerClass1=c1.new InnerClass1();
    }
}
内部类的最大作用

变相实现多重继承;

四、异常

  • 异常是程序中的一些错误,但并不是所有的错误都是异常,并且错误有时候是可以避免的。

    这些异常有的是因为用户错误引起,有的是程序错误引起的,还有其它一些是因为物理错误引起的。

  • 要理解Java异常处理是如何工作的,你需要掌握以下三种类型的异常:

    • **检查性异常:**最具代表的检查性异常是用户错误或问题引起的异常,这是程序员无法预见的。例如要打开一个不存在文件时,一个异常就发生了,这些异常在编译时不能被简单地忽略。
    • 运行时异常: 运行时异常是可能被程序员避免的异常。与检查性异常相反,运行时异常可以在编译时被忽略。
    • 错误: 错误不是异常,而是脱离程序员控制的问题。错误在代码中通常被忽略。例如,当栈溢出时,一个错误就发生了,它们在编译也检查不到的。
Exception 类的层次
  • Exception 类是 Throwable 类的子类。除了Exception类外,Throwable还有一个子类Error

  • 所有的异常类是从 java.lang.Exception 类继承的子类。

  • 异常类有两个主要的子类:IOException 类和 RuntimeException 类。

异常处理机制

Java异常处理:Java采用异常处理机制,将异常处理的程序代码集中在一起。

  1. 抓抛模型;
  2. 如果出现异常,会自动生成一个异常类对象,该异常对象将被提交给Java运行时系统,这个过程称为抛出异常(throw);
  3. 如果一个方法内抛出异常,该异常会被抛到调用方法中。这个过程称为捕获异常;(catch);
  4. 如果一个异常回到main()方法,并且main()方法没有处理,则程序运行终止;
  5. 程序员通常只能处理Exception,而对Error无能为力;
throws/throw 关键字
  • 如果一个方法没有捕获到一个检查性异常,那么该方法必须使用 throws 关键字来声明。throws 关键字放在方法签名的尾部。

  • 也可以使用 throw 关键字抛出一个异常,无论它是新实例化的还是刚捕获到的。

public void deposit(double amount) throws RemoteException
{
    // Method implementation
    throw new RemoteException();
}
捕获异常
  • 使用 try 和 catch 关键字可以捕获异常。try/catch 代码块放在异常可能发生的地方。

  • try/catch代码块中的代码称为保护代码

finally关键字
public static void main(String args[]){
    int a[] = new int[2];
    try{
        System.out.println("Access element three :" + a[3]);
    }catch(ArrayIndexOutOfBoundsException e){
        System.out.println("Exception thrown  :" + e);
    }
    finally{
        a[0] = 6;
        System.out.println("First element value: " +a[0]);
        System.out.println("The finally statement is executed");
    }
}
try-catch-finally与return
public static int test(){
    int a[] = new int[2];
    try{
        System.out.println("Access element three :" + a[3]);
        return 0;
    }catch(ArrayIndexOutOfBoundsException e){
        return -1;
    } finally{
        return 1;
    }
}//最终返回1;

五、集合

  • Java集合类存放在java.util包中,是一个用来存放对象的容器;
  • 集合只能存放对象;
  • 集合存放的是多个对象的引用;
  • 集合可以存放不同类型,不限数量的数据类型;

Java集合框架

Java集合框架主要包括两种类型的容器,一种是集合(Collection),另一种是图(Map)。

  1. Set:无序,不可重复;
  2. List:有序,可重复;
  3. Map:具有映射关系;

img

img

collection接口
public interface Collection<E> extends Iterable<E> 
  • 主要方法:
	
int size();
boolean isEmpty();
boolean contains(Object o);
Iterator<E> iterator();
Object[] toArray();
<T> T[] toArray(T[] a);
boolean add(E e);
boolean remove(Object o);
boolean containsAll(Collection<?> c);
boolean addAll(Collection<? extends E> c);
boolean removeAll(Collection<?> c);
default boolean removeIf(Predicate<? super E> filter) {
    Objects.requireNonNull(filter);
    boolean removed = false;
    final Iterator<E> each = iterator();
    while (each.hasNext()) {
        if (filter.test(each.next())) {
            each.remove();
            removed = true;
        }
    }
    return removed;
}
boolean retainAll(Collection<?> c);
void clear();
boolean equals(Object o);
int hashCode();
@Override
default Spliterator<E> spliterator() {
    return Spliterators.spliterator(this, 0);
}
default Stream<E> stream() {
    return StreamSupport.stream(spliterator(), false);
}
default Stream<E> parallelStream() {
    return StreamSupport.stream(spliterator(), true);
}
Set接口

同collection接口;

HashSet
public class HashSet<E>
    extends AbstractSet<E>
    implements Set<E>, Cloneable, java.io.Serializable

按Hash算法来存储集合中的元素;

  • 不能保证元素的排序顺序;
  • 不可重复;
  • 不是线程安全;
  • 集合元素可以使用null;

当向HashSet集合中存入一个元素时,会调用给对象的hashCode()方法来得到该对象的hashCode,然后根据hashCode决定该对象在HashSet中的存储位置;

如果两个元素的equals()方法返回true,但他们的hashCode()返回值不相等,hashSet将会把它们存储在不同的位置,但依然可以添加成功。

Set set=new HashSet();
set.add(1);
set.add("a");
set.add(1);

System.out.println(set.contains("a"));//是否包含
System.out.println(set.size());//大小
System.out.println(set.remove("A"));//false
set.remove("a");
//迭代器遍历
Iterator it=set.iterator();
while (it.hasNext()){
    System.out.print(it.next()+" ");
}

//for each 遍历
for (Object object:set) {
    System.out.print(object+" ");
}
//清空
set.clear();
TreeSet
public class TreeSet<E> extends AbstractSet<E>
    implements NavigableSet<E>, Cloneable, java.io.Serializable
  • TreeSet是SortedSet接口的实现类,TreeSet可以确保集合元素处于排序状态。

  • TreeSet支持两种排序方法:自然排序和定制排序。默认为自然排序;

  1. 自然排序:要求自定义类实现java.lang.Comparable接口并重写compareTo(Object obj)方法。在此方法中,指明按照自定义类的哪个属性进行排序,然后将集合按升序排列;
class Person implements Comparable<Person> {
    /**
     * 按年龄升序
     */
    @Override
    public int compareTo(Person o) {
        if(this.age>o.age){
            return 1;
        }else if(this.age<o.age){
            return -1;
        }else{
            return 0;
        }
    }
  1. 定制排序:在创建TreeSet集合对象时,提供一个comparator接口的实现类对象,由该对象负责集合元素的排序逻辑。其在类不可以修改时使用;

    在使用定制排序或是自然排序时,在其用到的类中都要重写hashCode()与equals()方法。

Comparator comparator=new Comparator() {
    @Override
    public int compare(Object o1, Object o2) {
        Person p1= (Person) o1;
        Person p2= (Person) o2;
        if(p1.age>p2.age){
            return -1;
        }else if(p1.age<p2.age){
            return 1;
        }else{
            return 0;
        }
    }
};
Set<Person> sp=new TreeSet<Person>(comparator);
List接口
  • 主要方法:
int size();
boolean isEmpty();
boolean contains(Object o);
Iterator<E> iterator();
Object[] toArray();
<T> T[] toArray(T[] a);
boolean add(E e);
boolean remove(Object o);
boolean containsAll(Collection<?> c);
boolean addAll(Collection<? extends E> c);
boolean removeAll(Collection<?> c);
boolean retainAll(Collection<?> c);
default void replaceAll(UnaryOperator<E> operator) {
    Objects.requireNonNull(operator);
    final ListIterator<E> li = this.listIterator();
    while (li.hasNext()) {
        li.set(operator.apply(li.next()));
    }
}
@SuppressWarnings({"unchecked", "rawtypes"})
default void sort(Comparator<? super E> c) {
    Object[] a = this.toArray();
    Arrays.sort(a, (Comparator) c);
    ListIterator<E> i = this.listIterator();
    for (Object e : a) {
        i.next();
        i.set((E) e);
    }
}
void clear();
boolean equals(Object o);
int hashCode();
E get(int index);
E set(int index, E element);
void add(int index, E element);
E remove(int index);
int indexOf(Object o);
int lastIndexOf(Object o);
ListIterator<E> listIterator();
ListIterator<E> listIterator(int index);
List<E> subList(int fromIndex, int toIndex);
@Override
default Spliterator<E> spliterator() {
    return Spliterators.spliterator(this, Spliterator.ORDERED);
}
ArrayList与Vector

区别:

  • Vector是一个古老的集合,通常建议用ArrayList;
  • Vector线程安全,ArrayList不安全;
  • 即使为保证List集合线程安全,也不推荐使用Vector;

例子:

List<Integer> list=new ArrayList<>();
list.add(16);
list.add(30);
list.add(10);
System.out.println(list);//[16, 30, 10]
System.out.println(list.get(0));
list.add(0,10);
System.out.println(list);//[10, 16, 30, 10]
List<Integer> l=new ArrayList<>();
l.add(22);
list.addAll(4,l);//指定插入下标
System.out.println(list);//[10, 16, 30, 10, 22]
System.out.println(list.subList(0,2));//fromIndex, toIndex
//[10, 16]
System.out.println(list.indexOf(10));//0
System.out.println(list.lastIndexOf(10));//3
list.remove(1);//[16, 30, 10, 22]
list.remove(Integer.valueOf(10));//[30, 10, 22]
list.set(2,3);//(index,value) index 不能越界 [30, 10, 3]
Map接口

同collection接口,

HashMap
Map<Integer,String> m=new HashMap<>();
m.put(1,"111");
m.put(2,"2222");
Set<Integer> keys=m.keySet();
Set<Map.Entry<Integer,String>> entries=m.entrySet();

System.out.println(m.containsKey(1));//true
System.out.println(m.containsValue("111"));//false
System.out.println(keys);//[1, 2]
for (Map.Entry<Integer,String> e:entries) {
    System.out.print("["+e.getKey()+","+e.getValue()+"]");
}
HashMap与HashTable

区别:

  • HashTable古老不建议;
  • HashTable线程安全,HashMap线程不安全;
  • HashTable不允许使用null作为key和value,HashMap 可以;

与HashSet一样不能保证key-value对的顺序;

TreeMap

​ 类似TreeSet,存储时根据Key对键值对进行排序;

  1. 自然排序:要求所有Key实现java.lang.Comparable接口并重写compareTo(Object obj)方法。在此方法中,指明按照自定义类的哪个属性进行排序,然后将集合按升序排列;

  2. 定制排序:在创建TreeSet集合对象时,提供一个comparator接口的实现类对象,由该对象负责集合元素的排序逻辑。其在类不可以修改时使用;

    在使用定制排序或是自然排序时,在其用到的类中都要重写hashCode()与equals()方法。

Collections工具类

排序:

List<String> l=new ArrayList<>();
l.add("a");
l.add("12");
Collections.reverse(l);//反转
Collections.sort(l);//根据元素的自然顺序按升序排序
Collections.shuffle(l);//随机排序
Collections.sort(l, new Comparator<String>() {
    @Override
    public int compare(String o1, String o2) {
        return 0;
    }
});//根据Comparator产生的顺序进行排序;
Collections.swap(l,0,1);//对集合中的i处元素和j处元素进行交换;

查找、替换:

Collections.max(l);
Collections.max(l, new Comparator<String>() {
    @Override
    public int compare(String o1, String o2) {
        return 0;
    }
});
Collections.min(l);
Collections.frequency(l,"a");
Collections.replaceAll(l,"a","b");

同步方法:

Collections类中提供了多个synchronizedXXX()方法,可使将指定集合包装成线程同步的集合;

六、泛型

只有指定类型才可以添加到集合中,类型安全;

Java泛型可以保证如果程序在编译时没有发出警告,运行时就不会产生classcatException异常。同时代码更加简洁、健壮。

Java中的泛型只在编译阶段有效。在编译过程中,正确检验泛型结果后,会将泛型的相关信息擦除,并且在对象进入和离开方法的边界处添加类型检查和类型转换的方法。也就是说,泛型信息不会进入到运行时阶段

泛型类

/**
 * 泛型类
 * 未传入泛型实参时,与泛型类的定义相同,在什么类的时候,需讲泛型的声明也一起加到类中;
 */
//class A implements IA<T>{//如果不声明泛型,编译器会报错;
class A<T> implements IA<T>{

泛型接口

/**
 * 泛型接口
 */
interface IA<T>{
    T test(T t);
}

泛型方法

/**
 * 泛型方法
 */
class Cc<E>{
    private E e;
    //在静态方法中,不能使用类定义泛型,如果要使用泛型,只能使用静态方法自己额外定义的泛型;
    public static <T> void teset4(T e){
        //System.out.println(this.e);//报错
    }
//    public static void teset5(E e){
//        System.out.println(this.e);//报错
//    }
    public <T> void test1(T s){
        T t=s;
    }
    public <T> T test2(T s){
        return s;
    }
    public <T> void test3(T... s){
        for (T t:s) {
            System.out.println(t);
        }
    }
}

通配符

  • <? extends Person> 只允许泛型为Person及Person的子类的引用调用;
  • <? super Person> 只允许泛型为Person及Person父类的引用调用;
  • <? extends Comparable> 只允许泛型为实现Comparable接口的实现类的引用调用;

枚举类

一个类的对象是有限而且固定的

手动实现枚举类
  • private修饰构造器;
  • 属性使用private final 修饰;
  • 吧该类的所有实例都使用public static final修饰;
枚举类和普通类的区别
  • 使用enum定义的枚举类默认继承了java.lang.Enum类;
  • 枚举类的构造器只能使用private访问控制符;
  • 枚举类的所有实例必须在枚举类中显式列出,以",“分割,”;" 结尾;
  • 所有的枚举类都提供了一个values方法,该方法可以很方便地遍历所有的枚举值;
enum Season implements IA{//可实现接口
    SPRING("春天","春暖花开"),//实例,相当于调用私有构造函数
    SUMMER("夏天","炎炎夏日"),
    AUTUMN("秋天","秋风萧萧"),
    WINTER("冬天","大雪纷飞");

    private final String name;
    private final String desc;
    private Season(String name, String desc) {
        this.name = name;
        this.desc = desc;
    }
    public String getName() {
        return name;
    }
    public String getDesc() {
        return desc;
    }
}
实现接口的枚举类
  • 可以实现一个或多个接口
  • 若需要每个枚举类在调用实现的接口方法呈现出不同的行为方式,可让每个枚举值分别来实现该方法;

注解

基本的Annotation
  • 使用Annotation时要在其前面加@符号,并把该Annotation当成一个修饰符使用。
  • 三个基本的Annotation:
    1. @Override:重写父类方法;
    2. @Deprecated:表示已过时;
    3. @SuppressWarnings:抑制警告;
自定义注解
  • @interface
  • 成员变量在Annotation定义中以无参数方法的形式来声明,其方法名和返回值定义了该成员的名字和类型;
  • 可以在定义成员变量时为其指定初始值,使用default关键字;
  • 没有成员定义的Annotation成为标记;包含成员变量的Annotation称为元数据Annotation;
/**
 * 1.访问修饰符必须为public,不写默认为public;
 * 2.该元素的类型只能是基本数据类型、String、Class、枚举类型、注解类型(体现了注解的嵌套效果)以及上述类型的一位数组;
 * 3.该元素的名称一般定义为名词,如果注解中只有一个元素,请把名字起为value(后面使用会带来便利操作);
 * 4.()不是定义方法参数的地方,也不能在括号中定义任何参数,仅仅只是一个特殊的语法;
 * 5.default代表默认值,值必须和第2点定义的类型一致;
 * 6.如果没有默认值,代表后续使用注解时必须给该类型元素赋值。
 *
 * 即又有属性的特征(可以赋值),又有方法的特征(打上了一对括号)
 */

//注解的生命周期有三个阶段:1、Java源文件阶段;2、编译到class文件阶段;3、运行期阶段。
// 默认的情况下,自定义注解是使用的RetentionPolicy.CLASS
@Retention(RetentionPolicy.RUNTIME)
//限定某个自定义注解能够被应用在哪些Java元素上面的
@Target(value = {ElementType.TYPE})
//指定自定义注解是否能随着被定义的java文件生成到JavaDoc文档
@Documented
public @interface AnnotationTest {
    public String name();
    int age() default 18;
    int[] array();
}

@AnnotationTest(name = "Test",array = {})
public class Test {
    public static void main(String[] args) {
        Test test=new Test();
        Class c=test.getClass();
        if(c.isAnnotationPresent(AnnotationTest.class)){
            System.out.println("Test类上配置了AnnotationTest注解!");
        AnnotationTest annotationTest= (AnnotationTest) c.getAnnotation(AnnotationTest.class);
        System.out.println("name:"+ annotationTest.name()+
                ",array:"+annotationTest.array()+
                ",age"+annotationTest.age());
        }else{
            System.out.println("没有配置AnnotationTest注解!");
        }
    }
}

七、线程

继承方式和实现方式的联系和区别:

  • 继承Thread:线程代码存放在Thread子类的run方法中;
  • 实现Runnable:线程代码存在接口的子类的run方法.

实现方法的好处:

  1. 避免了单继承的局限性;
  2. 多个线程可以共享同一个接口实现类的对象,非常适合多个相同线程来处理同一份资源。

Thread类的方法

void start();
run();
String getName();
void setName();
static currentThread();
setPriority();//设置线程的优先级
join(); // 常用方法,等待线程结束,调用线程阻塞直到join()线程执行完;
yield();// 常用方法,让出cpu时间片,暂停当前执行的线程,把执行机会让给优先级相同或更高的线程;
sleep(); // 常用方法,线程休眠,时间到后重排队;

interrupt(); // 常用方法,中断线程
boolean isInterrupted(); // 常用方法,返回线程是否被中断
static boolean interrupted(); // 常用方法,返回线程是否被中断,同时清空中断标记
boolean isAlive(); //判断是否还活着;
holdsLock(Object obj); // 常用方法,线程是否持有锁
setContextClassLoader(); // 常用方法,设置线程上下文类加载器

stop(); // 过期方法,停止线程的执行,强制结束;
suspend(); // 过期方法,暂停线程的执行
resume() // 过期方法,恢复线程的执行

例子:

Thread t1=new TestThread1();
System.out.println(t1.getPriority());

TestThread2 testThread2=new TestThread2();
Thread t2=new Thread(testThread2);
t2.setPriority(10);
t1.start();

线程的生命周期

  • 新建(new):Thread类或其子类的对象被声明并创建后,**还没有在其上调用start()**方法;
  • 可运行状态 / 就绪(Runnable):当线程有资格运行,但调度程序还没有把它选定为运行线程时线程所处的状态。当**start()**方法调用时,线程首先进入可运行状态。在线程运行之后或者从阻塞、等待或睡眠状态回来后,也返回到可运行状态。
  • 运行:**run()**方法定义了线程的操作和功能;线程调度程序从可运行池中选择一个线程作为当前线程时线程所处的状态。这也是线程进入运行状态的唯一一种方式。
  • 等待/阻塞/睡眠:被人为挂起或执行输入输出操作时,让出CPU并临时中止自己的执行;这是线程有资格运行时它所处的状态。实际上这个三状态组合为一种,其共同点是:线程仍旧是活的,但是当前没有条件运行。
  • 死亡:线程完成全部工作或线程被提前强制中止;当线程的run()方法完成时就认为它死去。这个线程对象也许是活的,但是,它已经不是一个单独执行的线程。线程一旦死亡,就不能复生。如果在一个死去的线程上调用start()方法,会抛出java.lang.IllegalThreadStateException异常

img

线程的同步与死锁

Synchronized
  1. 对象锁,同一个对象一个锁
    • 同步普通方法
    • synchronized(对象){} 代码块
  2. 类锁,同一类一个锁
    • 同步静态方法
    • synchronized(类名.class){} 代码块
死锁
  • 不同线程分别占用对方需要的同步资源不放弃,都在等待对方放弃自己需要的同步资源,就形成了线程的死锁;
  • 解决方法:
    1. 专门的算法、原则
    2. 尽量减少同步资源的定义
线程通信
  • wait():令当前线程挂起并放弃CPU、同步资源,使别的线程可访问并修改共享资源,而当前线程排队等候再次对资源的访问;
  • notify():唤醒正在排队等待同步资源的线程中优先级最高者结束等待;
  • notifyAll():唤醒正在排队等待资源的所有线程结束等待;

Java.lang.Object提供的这三个方法只有在synchronized方法或代码块中才能使用,否则会报java.lang.illegalMonitorStateException异常;

消费者和生产者

使用wait()与notify()方法进行通信;

八、Java IO

8.1 主要内容

  • java.io.File类的使用;

  • IO原理及流的分类;

  • 文件流(基于文件)

    FileInputStream / FileOutputStream / FileReader / FileWriter;

  • 缓冲流(基于内存)

    BufferedInputStream / BufferedOutputStream / BufferedReader / BufferedWriter;

  • 转换流

    InputStreamReader / OutputStreamWriter

  • 标准输入/输出

  • 打印流(了解)

    PrintStream / PrintWriter

  • 数据流(了解)

    DataInputStream / DataOutStream

  • 对象流 ----涉及序列化、反序列化

    ObjectInputStream / ObjectOutputStream

  • 随机存取文件流

    RandomAccessFile

通过程序把一个图放到一个文件夹,把图片转化为一个数据集(例如二进制),把这些数据一点一点传到文件夹,类似水的流动,称这个整体的数据集是一个数据流;

8.2 File类

  • 文件和目录路径名的抽象表示形式,与平台无关;

  • File能新建、删除、重命名文件和目录,但不能访问文件内容本身;如果需要访问文件内容本身,需要使用输入输出流;

  • File 对象可以作为参数传递给流的构造函数;

  • File类的常见构造方法:

    public File(String pathname);//绝对路径或相对路径;

    public File(String parent,String child);//parent为父路径,child为子路径创建File对象;

访问文件名
  • getName() //文件或文件夹名
  • getPath() //获取文件或文件夹的路径,就是new file时写的;
  • getAbsoluteFile() //用当前文件的绝对路径构建的file对象
  • getAbsolutePath() //当前文件的绝对路径
  • getParent() //父级路径
  • renameTo(File newName) //给文件或文件夹重命名
文件检测
  • exist()
  • canWrite()
  • canRead()
  • isFile()
  • isDirectory()
获取常规文件信息
  • lastModify() //最后修改时间
  • Length() //文件长度,字节数
文件操作相关
  • createNewFile() //创建新的文件
  • delete() //删除文件
目录操作相关
  • mkDir() //创建文件夹
  • list() //获取当前目录的子集的名称
  • listFiles() //获取当前目录的子集的File对象
递归遍历文件
/**
 * 递归遍历文件
 */
public static void test(File file){
    if(file.isFile()){
        System.out.println(file.getAbsolutePath()+"是文件");
    }else{
        System.out.println(file.getAbsolutePath()+"是文件夹");
        File[] fs=file.listFiles();
        if(fs!=null&&fs.length>0){
            for (File f : fs) {
                test(f);
            }   
        }
    }
}

8.3 JavaIO原理

  • 输入input:读取外部数据(磁盘光盘等)到程序(内存)中;
  • 输出output:读取程序数据(内存)到外部数据(磁盘光盘等)中;

8.4 流的分类

  • 按数据单元分:字节流(8 bit)和字符流(16bit);
  • 按数据流的流向分:输入流(Input,Reader),输出流(Output,Writer);
8.4.1 文件流
文件字节流
  1. 文件字节输入流

    try {
        FileInputStream in=new FileInputStream("D:\\com\\yty\\demo\\_12_io\\test1.txt");
        byte[] b=new  byte[1024];
        int len=0;
        //read(byte b[]) //返回读取的数据的长度,如果读取到最后一个数据,还会向后读一个,返回-1;
        while ((len=in.read(c))!=-1){
            System.out.println(new String(b,0,len));
            //String(byte bytes[], int offset, int length),参数1是缓冲数据的数组,参数2是从数组的那个位置开始转化字符串,参数3是
            //总共转化;
        }
        in.close();
    } catch (Exception e) {
        e.printStackTrace();
    }
    
  2. 文件字节输出流

    try {
        FileOutputStream out=new FileOutputStream("D:\\IdeaProjects\\javaStudy-demo\\src\\com\\yty\\demo\\_12_io\\out.txt");
        String s="aaaaaaaaddddd";
        out.write(s.getBytes());
        out.flush();
        out.close();
    } catch (Exception e) {
        e.printStackTrce();
    }
    
  3. 字节流复制文件

    public static void copy(String sourcePath,String targetPath){
        try {
            FileInputStream in=new FileInputStream(sourcePath);
            FileOutputStream out=new FileOutputStream(targetPath);
            byte[] b=new byte[1024];
            int len=0;
            while ((len=in.read(c))!=-1){
                out.write(b,0,len);
            }
            out.flush();
            out.close();
            in.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
文件字符流
  1. FileReader fileReader=new FileReader(path);
    char[] c=new char[10];
    int len=0;
    System.out.println("-------fileReaderTest-------");
    while ((len=fileReader.read(c))!=-1){
        System.out.println(new String(c,0,len));
    }
    fileReader.close();
    
  2. FileWriter fileWriter=new FileWriter(path);
    fileWriter.write(txt);
    fileWriter.flush();
    fileWriter.close();
    
8.4.2 缓冲流
  1. BufferedInputStream和BufferedOutputStream;

    FileInputStream in=new FileInputStream(path);
    BufferedInputStream bi=new BufferedInputStream(in);
    
    FileOutputStream out=new FileOutputStream(path);BufferedOutputStream bo=new BufferedOutputStream(out);
    
  2. BufferedReader和BufferedWriter;

8.4.3 转换流

提供了在字节流和字符流之间的转换;

InputStreamReaderOutputStreamWriter;

字节流中的数据都是字符时,转成字符流操作更高效;

构造方法:

  • InputStreamReader(InputStream in);

  • InputStreamReader(InputStream in,String charsetName);

//字节输入流转化为字符输入流
public static void testInputStreamReader(String path){
    try {
        FileInputStream fs=new FileInputStream(path);
        InputStreamReader isr=new InputStreamReader(fs);
        char[] c=new char[10];
        int len=0;
        while ((len=isr.read(c))!=-1){
            System.out.println(new String(c,0,len));
        }
        isr.close();
        fs.close();

    } catch (Exception e) {
        e.printStackTrace();
    }
}
//转换字节输出流为字符输出流
public static void testInputStreamReader(String path,String txt){
    try {
        FileOutputStream out=new FileOutputStream(path);
        OutputStreamWriter osw=new OutputStreamWriter(out);
        String s=txt;
        osw.write(s);//void write(byte b[])
        osw.flush();
        osw.close();
        out.close();
    } catch (Exception e) {
        e.printStackTrace();
    }
}
8.4.4 标准输入输出流
  • System.in类型是InputStream;
  • System.out是PrintStream,是OutputStream的子类FilterOutputStream的子类;
InputStreamReader in=new InputStreamReader(System.in);//字节输入流转为字符输入流
BufferedReader br=new BufferedReader(in);//缓冲字符输入流
BufferedWriter out=new BufferedWriter(new FileWriter(tpath));//缓冲字符输出流

String line="";
while ((line=br.readLine())!=null){
    if(line.equals("over")){
        break;
    }
    out.write(line);
}
out.flush();
out.close();
br.close();
in.close();
8.4.5 打印流
  • PrintStream(字节打印流),PrintWriter(字符打印流)
  • 不会抛出异常;
  • 有自动flush功能;
  • System.out返回的是PrintStream的实例;
8.4.6 数据流
  • DataInputStreamDataOutStream
  • 分别套接在InputStream和OutPutStream节点流上;
8.4.7 对象流
  • ObjectInputStreamObjectOutStream
  • 序列化:ObjectOutStream将java对象写入IO流;
  • 反序列化:ObjectInputStream从IO流中恢复该java对象;
  • 不能序列化static和transient修饰的成员变量;
8.4.8 RandomAccessFile类
  • 支持直接跳到文件的任意地方来读写文件;
  • 可以向已存在的文件后追加内容;
  • long getFilePointer():获取文件记录指针的当前位置;
  • void seek(long pos):将文件记录指针定位到pos位置;

九、反射

概述

反射机制,就是通过一个抽象的类名能够在加载类的内存中找到相匹配的类的具体信息。

java能够反射的前提
  • 已经加载过这个类,就可以通过类名来寻找到这个类的所有相关信息;

Reflection是被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息,并能够直接操作任意对象的内部属性和方法;

提供的功能
  • 在运行时判断任意一个对象所属的类
  • 在运行时判断任意一个类所具有的成员变量和方法
  • 在运行时构造任意一个类的对象
  • 在运行时调用任意一个对象的成员变量和方法
  • 生成动态代理;
主要API
  • java.lang.Class:代表一个类;
  • java.lang.reflect.Method:代表类的方法;
  • java.lang.reflect.Field:代表类的成员变量;
  • java.lang.reflect.Constructor:代表类的构造方法;

9.1 Class类

在Object类中定义了以下方法,次方法将被所有子类继承:

public  final Class getClass()

Class类是java反射的源头,可以通过对象反射求出类的名称;对随意的类进行高度的抽象,形成一个可以描述所有类的类;

  • Class本身也是一个
  • Class对象只能由系统建立对象
  • 一个类在JVM中只会有一个Class实例
  • 一个Class对象对应的是一个加载到JVM中的一个**.class文件**;
  • 每个类的实例都会记得自己是由哪个class实例所生成
  • 通过Class可以完整地得到一个类中的完整结构
Class类的常用方法
  1. static Class forName(String name)
  2. Object newInstance()
  3. getName()
  4. getSimpleName
  5. Class getSuperClass()
  6. Class[] getInterfaces()
  7. Classloader getClassloader()
  8. Constructor[] getConstructors()
  9. Field getField(“age”)
  10. Field getDeclaredField(“age”)
  11. Field[] getFields()
  12. Field[] getDeclaredFields()
  13. Method getMethod(“getName”,String.class)
  14. Method getDeclaredMethod(“getName”,String.class)
  15. Method[] getMethods() //获取当前类及所有继承的父类的public修饰的方法
  16. Method[] getDeclaredMethods() //获取当前类的所有方法
public static void main(String[] args) throws NoSuchFieldException, NoSuchMethodException, ClassNotFoundException, IllegalAccessException, InstantiationException {
    Person p=new Person();
    Class c=p.getClass();
    System.out.println(c);

    Class c2=Class.forName("com.yty.demo._4_oop.Person");//
    Person p2= (Person) c.newInstance();//创建目标类对象
    c.getName();//类的全名(包名+类名)
    c.getSimpleName();//类名
    Class superC=c.getSuperclass();//获取父类
    Class[] superI=c.getInterfaces();//获取所有实现的接口
    ClassLoader classLoader=c.getClassLoader();//类加载器
    Constructor[] constructor=c.getConstructors();//构造器

    //Field getField(String name)
    // 自身及继承的public属性
    Field field1=c.getField("hobby");
    //自身的任何权限的属性
    Field field2=c.getDeclaredField("yearsOld");
    Field[] fields1=c.getFields();
    Field[] fields2=c.getDeclaredFields();

    //Method getMethod(String name, Class<?>... parameterTypes)
    //获取当前类及所有继承的父类的public修饰的方法。仅包括public
    Method method1=c.getMethod("setSex",int.class);
    //获取当前类的所有方法,包括public/private/protected/default修饰的方法。但不包括继承的方法
    Method method2=c.getDeclaredMethod("employee");
}
实例化Class类对象
  1. 通过类的class属性获取;Class c=String.class

  2. 调用实例的getClass()方法;Class c=p.getClass();

  3. 已知类的全类名,Class.forName()方法;Class c2=Class.forName(“com.yty.demo._4_oop.Person”);

  4. 通过ClassLoader获取

    ClassLoader cl=this.getClass().getClassLoader();

    Class c=cl.loadClass(“全类名”);

9.2 通过反射调用类的完整结构

  1. 实现的全部接口;Class[] superI=c.getInterfaces();

  2. 所继承的父类;Class superC=c.getSuperclass();

  3. 全部的构造器;Constructor[] constructor=c.getConstructors();

  4. 全部的方法;

    Method[] methods1=c.getMethods();

    Method[] methods2=c.getDeclaredMethods();

  5. 全部的Field;

    Field[] fields1=c.getFields(); –自身及继承的public属性

    Field[] fields2=c.getDeclaredFields(); –自身的所有属性

  6. 类所在的包

    Package getPackage();

Field相关方法
String getName();//名称
int getModifiers();//修饰符
Class<?> getType()//类型
void setAccessible(boolean flag);///操作私有Field前先设置可被外部访问
void set(Object obj, Object value);
Object get(Object obj);
Method相关方法
String getName();
Class<?>[] getParameterTypes();
int getModifiers();//修饰符
Class<?> getReturnType();
void setAccessible(boolean flag);///调用私有方法前先设置可被外部访问
Object invoke(Object obj, Object... args);//(被调用的对象,方法参数);

9.3 Java动态代理

proxy:专门完成代理的操作类,是所有动态代理的父类。通过此类为一个或多个接口动态的生成实现类。

创建一个动态代理类所对应的Class的对象;

static Object newProxyInstance(classLoader,interfaces,Handler)

当代理对象调用真实对象的方法时,其会自动的跳转到代理对象关联的handler对象的invoke方法来进行调用。

/**
 * 在执行方法时需要加入一些东西
 */
InvocationHandler handler=new ProxyDemo(testDemo);
//Object newProxyInstance(classLoader,interfaces,Handler)
//创建动态代理对象
ITestDemo t= (ITestDemo) Proxy.newProxyInstance(handler.getClass().getClassLoader(),
        testDemo.getClass().getInterfaces(),handler);
t.test1();
t.test2();
//实现接口InvocationHandler的类,实现invoke方法
public class ProxyDemo implements InvocationHandler {
    Object obj;//被代理的对象
    public ProxyDemo(Object obj) {
        this.obj = obj;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("========方法开始执行=======");
        Object res=method.invoke(this.obj,args);
        System.out.println("========方法执行结束=======");
        return res;
    }
}
//被代理对象
public class TestDemoImpl implements ITestDemo {
    @Override
    public void test1() {
        System.out.println("执行方法 test1 ");
    }
    @Override
    public void test2() {
        System.out.println("执行方法 test2 ");
    }
}
public interface ITestDemo {
    void test1();
    void test2();
}

1=c.getMethods();

Method[] methods2=c.getDeclaredMethods();

  1. 全部的Field;

    Field[] fields1=c.getFields(); –自身及继承的public属性

    Field[] fields2=c.getDeclaredFields(); –自身的所有属性

  2. 类所在的包

    Package getPackage();

Field相关方法
String getName();//名称
int getModifiers();//修饰符
Class<?> getType()//类型
void setAccessible(boolean flag);///操作私有Field前先设置可被外部访问
void set(Object obj, Object value);
Object get(Object obj);
Method相关方法
String getName();
Class<?>[] getParameterTypes();
int getModifiers();//修饰符
Class<?> getReturnType();
void setAccessible(boolean flag);///调用私有方法前先设置可被外部访问
Object invoke(Object obj, Object... args);//(被调用的对象,方法参数);

9.3 Java动态代理

proxy:专门完成代理的操作类,是所有动态代理的父类。通过此类为一个或多个接口动态的生成实现类。

创建一个动态代理类所对应的Class的对象;

static Object newProxyInstance(classLoader,interfaces,Handler)

当代理对象调用真实对象的方法时,其会自动的跳转到代理对象关联的handler对象的invoke方法来进行调用。

/**
 * 在执行方法时需要加入一些东西
 */
InvocationHandler handler=new ProxyDemo(testDemo);
//Object newProxyInstance(classLoader,interfaces,Handler)
//创建动态代理对象
ITestDemo t= (ITestDemo) Proxy.newProxyInstance(handler.getClass().getClassLoader(),
        testDemo.getClass().getInterfaces(),handler);
t.test1();
t.test2();
//实现接口InvocationHandler的类,实现invoke方法
public class ProxyDemo implements InvocationHandler {
    Object obj;//被代理的对象
    public ProxyDemo(Object obj) {
        this.obj = obj;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("========方法开始执行=======");
        Object res=method.invoke(this.obj,args);
        System.out.println("========方法执行结束=======");
        return res;
    }
}
//被代理对象
public class TestDemoImpl implements ITestDemo {
    @Override
    public void test1() {
        System.out.println("执行方法 test1 ");
    }
    @Override
    public void test2() {
        System.out.println("执行方法 test2 ");
    }
}
public interface ITestDemo {
    void test1();
    void test2();
}

img

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐