java学习笔记

数组

1.数组的定义和声明

数组(引用数据类型)即为多个相同类型数据按一定顺序排列形成的内容。并且用一个名字命名,通过编号的方式对这些数据进行统一管理。

数组组成

数组名元素下标、角标、索引数组的长度:元素的个数数组的特点:

有序数组属于引用数据类型,而数组的元素既可以是引用,也可以是基本数据类型。。数组的长度一旦确定,就不能修改。创建数组对象会在内存中开辟一整块连续的空间。数组的分类:

按照维数:一维、二维按照数组元素的类型:基本数据类型、引用数据类型一维数组的使用:

一维数组的声明和初始化如何调用数组的指定位置的元素如何获取数组的长度如何遍历数组数组元素的默认初始化值数组的内存解析

public class shuzu1 {
    public static void main(String[] agrs) {
//        一维数组的声明和初始化
        int[] num; //声明
        //静态初始化:数组的初始化和数组元素的赋值同时进行
        num = new int[]{1001, 1002};
        //动态初始化:数组的初始化和数组元素的赋值分开进行
        String[] names = new String[5];
​
        //错误的写法
        //int[] arr1 = new int[];
        //int[5] arr2 = new int[5];
        //总结数组一旦初始化完成,长度就确定了。
//       这种写法也是正确的,类型推断
        int[] arr8 = {1,2,3,4};
//      2.如何调用数组的指定位置的元素:通过角标的方式调用
        names[0] = "kh";//从0开始
        names[1] = "ml";
        names[2] = "17";
        names[3] = "yt";
        names[4] = "mt";
//      names[5] = "mt"; 越界
//      3.如何获取数组的长度
        System.out.println(names.length);
//      4.如何遍历数组
//        System.out.println(names[0]);
        for (int i = 0; i < names.length; i++) {
            System.out.println(names[i]);
        }
//      5.数组元素的默认初始化值
//       整形数据元素默认为0;
//       char类型数据元素默认为0(ascii码)
//       boolean布尔型默认值是false
//        引用数据类型的默认值是null
        int[] arr = new int[5];
        for (int s = 0; s < arr.length; s++) {
            System.out.println(arr[s]);
        }
        System.out.println("*************");
        char[] arr1 = new char[5];
        for (int f = 0; f < arr.length; f++) {
            System.out.println(arr1[f]);
            System.out.println("----" + arr1[f] + "----");
        }
        System.out.println("*************");
        boolean[] arr3 = new boolean[5];
        System.out.println(arr3[1]);
​
        System.out.println("*************");
        String[] arr4 = new String[5];
        System.out.println(arr4[1]);
//      6.数组的内存解析
//        栈与堆的关系
    }
}
​
​

2.二维数组的创建

​
二维数组的创建
​
public class shuzu21 {
    public static void main(String[] args) {
//  1.二维数组的声明和初始化
        int[] arr = new int[]{1, 2, 3};//一维数组
//     静态初始化
        int[][] arr1 = new int[][]{{1, 2, 3}, {4, 5}};
//     动态初始化
        int[][] arr2 = new int[4][5];
        String[][] arr3 = new String[4][2];
        String[][] arr4 = new String[4][];
//       类型推断
        int[][] arr5 = {{1, 2, 3, 4}, {2, 4}};
​
//       2.如何调用二维数组的指定位置的元素
        System.out.println(arr1[0][2]);//3
        System.out.println(arr3[0][0]);//null
        arr4[0]=new String[4];
        System.out.println(arr4[0][0]);//如果不先定义,就为空指针
​
//       3.获取数组的长度
        System.out.println(arr5.length);//2
        System.out.println(arr5[0].length);//4
//       4.如何遍历二维数组(两层for循环)
        System.out.println("_____________");
        for(int i=0;i<arr5.length;i++){
            for(int j=0;j<arr5[i].length;j++){
                System.out.print(arr5[i][j]+"  ");
            }
            System.out.println();
        }
​
    }
}
​
二维数组的使用
​
public class shuzu22 {
    public static void main(String[] args) {
//  二维数组的使用
//  分为外层数组元素和内层数组元素
//
//  5.二维数组的元素默认初始值
      /* 写法一 int[][] arr = new int[4][3];
        外层元素初始化值为地址值
        内层元素初始化值等同一维数组类型
        写法二 String[][] arr2 = new String[4][];
        外层元素 初始化值为null
        内层元素初始化值为不能调用,否则报错
        */
        int[][] arr = new int[4][3];
        System.out.println(arr[0]);//[I@119d7047地址值
        System.out.println(arr[0][0]);//0
        System.out.println(arr);//[[I@776ec8df
​
        System.out.println("----------------");
​
        String[][] arr1 = new String[4][4];
        System.out.println(arr1[1]);//地址值
        System.out.println(arr1[1][1]);//null
​
        System.out.println("----------------");
        String[][] arr2 = new String[4][];
        System.out.println(arr2[2]);//null
//      System.out.println(arr2[2][1]);空指针异常
        /*
        6.二位数组的内存解析
​
​
        * */
    }
}
​
在这里插入代码片

3.数组练习

​
数组练习题
​
import java.util.Scanner;
​
public class szlianxi1 {
    public static void main(String[] args) {
//      1.读取学生个数
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入学生人数:");
        int number = scanner.nextInt();
//      2.创建数组存储学生成绩,动态初始化
        int[] scores = new int[number];
//      3.给数组中的元素赋值
        int max = 0;
        System.out.println("请输入" + number + "个学生成绩");
        for (int i = 0; i < scores.length; i++) {
            scores[i] = scanner.nextInt();
            /*if (max < scores[i]) {
                max = scores[i];可以先在此处求出最大值
            }*/
        }
//      4.获取数组中的最大值
​
        for (int n = 0; n < scores.length; n++) {
            if (max < scores[n]) {
                max = scores[n];
            }
        }
​
​
//      5.根据成绩输出成绩并且判定等级
        char level;
        for (int d = 0; d < scores.length; d++) {
            if (max - scores[d] <= 10) {
                level = 'A';
            } else if (max - scores[d] <= 20) {
                level = 'B';
            } else if (max - scores[d] <= 30) {
                level = 'C';
            } else {
                level = 'E';
            }
            System.out.println("student "+d+"score is "+" "+scores[d]+"grade is"+level);
        }
    }
}
练习题2 杨辉三角
​
public class szlianxi2 {
    public static void main(String[] args) {
        int[][] arr=new int[][]{{3,5,8},{12,9},{7,0,6,4}};
        int sum = 0;
        for(int i = 0;i<arr.length;i++){
            for(int j = 0;j<arr[i].length;j++){
                sum+=arr[i][j];
            }
        }
        System.out.println(sum);
//      杨辉三角
        //1.声明一个二维数组
        int[][] yanghui=new int[10][];
        //2.赋值
        for(int i=0;i<yanghui.length;i++){
            yanghui[i]=new int[i+1];
            //2.1给首末元素赋值
            yanghui[i][0]=yanghui[i][i]=1;
            //2.2给每行的非首末元素赋值
            if(i>1){
                for(int j=1;j<yanghui[i].length-1;j++){
                    yanghui[i][j]=yanghui[i-1][j-1]+yanghui[i-1][j];
                }
            }
        }
​
        //3.遍历
        for(int i=0;i<yanghui.length;i++){
            for(int j=0;j<yanghui[i].length;j++){
                System.out.print(yanghui[i][j]+" ");
​
            }
            System.out.println();
​
        }
    }
}
​
在这里插入代码片

基础算法

简单了解基本算法

数值操作

public class suanfa1 {
    public static void main(String[] args) {
//   算法考察:求最大值最小值平均数总和
//    [10,99]
//    (int)(math.random()*(99-10+1)+10)
        int[] arr = new int[10];
        for(int i = 0;i<arr.length;i++){
            arr[i]=(int)(Math.random()*(99-10+1)+10);
        }
//        遍历数组
        for(int i = 0;i<arr.length;i++){
            System.out.print(arr[i]+"  ");
        }
        System.out.println();
//      求最大值
        int maxvalue = arr[0];//不写0是因为可能存在负数存在
        for(int i = 1;i<arr.length;i++){
            if(arr[i]>maxvalue){
                maxvalue = arr[i];
            }
        }
        System.out.println("最大值为:"+maxvalue);
​
//        求最小值
        int minvalue = arr[0];//不写0是因为可能负数存在
        for(int i = 1;i<arr.length;i++){
            if(arr[i]<minvalue){
                minvalue = arr[i];
            }
        }
        System.out.println("最小值为:"+minvalue);
​
//       求总和
        int sum = 0;
        for(int i = 0;i<arr.length;i++){
            sum+=arr[i];
        }
        System.out.println("总和为:"+sum);
​
//        求平均值
        double ave = 0;
        ave = sum/arr.length;
        System.out.println("平均值为:"+ave);
​
    }
​
}
数值的赋值
​
public class suanfa2 {
    public static void main(String[] args) {
        /*
        修改arr2的值导致arr1的值发生改变
        是因为将arr1的值赋给了arr2
        而arr1中存放的是堆中的地址
        相当于c语言中的指针
        * */
//        建立arr1数组
        int[] arr1,arr2 ;
        arr1 = new int[]{2,3,5,7,11,13,17,19};
        for(int i = 0;i<arr1.length;i++){
            System.out.print(arr1[i]+"\t");
        }
        System.out.println();
//        给arr2数组赋值
//        不能称作数组的复制,相当于是一个快捷方式
       arr2 = arr1;
//        修改arr2中的偶索引元素,使其等于索引值
        for(int i=0;i<arr2.length;i=i+2){
            arr2[i]=i;
        }
        for(int i = 0;i<arr1.length;i++){
            System.out.print(arr1[i]+"\t");
        }
        
    }
}
数组的复制与反转、二分法查找、线性查找
​
```java
    public static void main(String[] args) {
        /*
        * 数组的复制
        *数组的反转
        * */
        //  建立arr1/arr2数组
        int[] arr1,arr2 ;
        arr1 = new int[]{2,3,5,7,11,13,17,19};
        for(int i = 0;i<arr1.length;i++){
            System.out.print(arr1[i]+"\t");
        }
        System.out.println("----------");
​
//        将arr1中的值复制到arr2
        arr2 = new int[arr1.length];
        for(int i = 0;i<arr1.length;i++){
            arr2[i]=arr1[i];
        }
//       打印arr2
        for(int i = 0;i<arr1.length;i++){
            System.out.print(arr2[i]+"\t");
        }
        System.out.println("------");
//      数组的反转方法一
//        for(int i =0;i<arr1.length;i++){
//          for(int i =0;i<arr1.length/2;i++){
//            int temp = arr1[i];
//            arr1[i]=arr1[arr1.length-i-1];
//            arr1[arr1.length-i-1]=temp;
//         }
//        数组的反转方法二
        for(int i = 0,j=arr1.length-1;i<j;i++,j--){
            int temp = arr1[i];
            arr1[i]=arr1[j];
            arr1[j]=temp;
        }
//        方法三是通过两个数组完成
//       for(int i = arr2.length-1;i>=0;i--){
//
//                arr1[arr2.length-i-1]=arr2[i];
//        }
        for(int i = 0;i<arr1.length;i++){
            System.out.print(arr1[i]+"\t");
        }
        System.out.println("---------");
//      数组的查找
//      线性查找
        boolean isFlag = true;
        int dest = 7;
        for(int i = 0;i<arr1.length;i++){
            if(arr1[i]==dest){
                System.out.println(i);
                isFlag = false;
                break;
            }
        }
        if(isFlag){
            System.out.println("不好意思没有找到");
        }
//     二分法查找:(所查找的数组必须有序)
        int dest1=5;
        int head = 0;
        int end = arr1.length-1;
        boolean isFlag1=true;
        while(head<=end){
            int middle = (head+end)/2;
            if(dest1==arr1[middle]){
                System.out.println("找到了"+middle);
                isFlag=false;
            }else if(arr1[middle]>dest1){
                end=middle-1;
            }else{//rr2[middle]<dest1)
                head=middle+1;
            }
        }
        if(isFlag){
            System.out.println("不好意思没有找到");
        }
//       排序算法
​
    }
}
数组的冒泡排序
​
public class bubble {
    public static void main(String[] args) {
        int[] arr = new int[]{12,2,3,56,45,23,67,42};
//        冒泡排序
        for(int i=0;i<arr.length-1;i++){
            for(int j=0;j<arr.length-1-i;j++){
                if(arr[j]>arr[j+1]){
                    int temp = arr[j];
                    arr[j]=arr[j+1];
                    arr[j+1]=temp;
                }
            }
        }
        for(int i = 0;i<arr.length;i++){
            System.out.print(arr[i]+"\t");
        }
//        boolean equals(int[] a,int[] b);
​
    }
}

面向对象上

1. 创建与设计类

public class duixiang1 {
    /*一、设计类就是设计类的成员
     * 属性=成员变量=field=域
     * 方法=成员方法=函数=method
     * 创建类的对象=类的实例化
     * 二、类和对象的使用(面向对象思想落地的实现)
     * 1.创建类,设计类的成员
     * 2.创建类的对象
     * 3.调用对象的属性和方法
     * 三、创建类的多个对象,每个对象有自己独立的属性
     * 意味着,修改一个对象的属性a,不影响另外一个对象属性a的值
     * 四、对象的内存解析
     * 栈:局部变量、指向堆的地址 堆:实体、属性
     * */
    public static void main(String[] args) {
//        创建person类的对象
        Person p1 = new Person();
//        调用对象的结构:属性、方法
//        调用属性:”对象.属性“
        p1.name = "angela";
        p1.age = 13;
​
        System.out.println(p1.age);
//       调用方法:“对象.方法”
        p1.eat();
        p1.talk("chinese");
//        ***************************
        Person p2 = new Person();
        System.out.println(p2.name);//null
//        *****************
        Person p3 = p1;//将p1中存放的堆地址给了p3
        System.out.println(p3.name);
        p3.age=8;
        System.out.println(p1.age);//修改p3同时p1也会改变
    }
}
​
class Person {
    //      属性
    String name;
    int age = 1;
    boolean isMale;
​
    //      方法
    public void eat() {
        System.out.println("人可以吃饭");
​
    }
    public void talk(String language) {
        System.out.println("人可以说话,使用的是:" + language);
​
    }
}
​

2.类的属性的使用以及属性和局部变量的比较

public class duixiang2 {
    /*
     * 类中属性的使用
     *
     * 属性(成员变量) vs 局部变量
     * 1.相同点
     *   1.1定义变量格式相同
     *   1.2先声明,后使用
     *   1.3都有它对应的作用域
     *
     * 2.不同点
     *   2.1在类中声明的位置不同
     *   属性:直接定义在类的一对{}中
     *   局部变量:声明在方法内、方法形参、代码块内、构造器形参、构造器内部变量
     *   2.2关于权限修饰符的不同
     *   属性:可以在声明属性时,指明其权限,使用权限修饰符。
     *   常用的权限修饰符:private、public、缺省、protected--->封装性
     *   目前我们使用缺省
     *   局部变量不能使用权限修饰符
     *   2.3默认初始化值的情况
     *   属性:类的属性,根据其类型,都有默认初始化值
     *   数字型:0 String:null boolean:false
     *   局部变量:没有默认初始化值
     *   调用之前必须赋值
     *   2.4在内存中加载的位置
     *   属性:加载到堆空间
     *   局部变量:加载到栈空间
     *
     * */
    public static void main(String[] args) {
        User p1 = new User();
        System.out.println(p1.name);
        System.out.println(p1.age);
        System.out.println(p1.isMale);
        p1.talk("llf");
    }
}
​
class User {
    //    定义属性(成员变量)
    String name;
    int age;
    boolean isMale;
​
    //    定义方法
    public void talk(String xox) {//形参、局部变量
        System.out.println("我喜欢" + xox);
    }
​
    public void eat() {
        String food = "passion friut" ;//局部变量
        System.out.println("我们喜欢吃" + food);
​
    }
}

3.数组工具类

public class shuzugjl {
    public static void main(String[] args) {
        shuzugjl s = new shuzugjl();
        int[] arr = new int[]{1, 2, 4, 5, 44, 3, 46, 89};
        s.print(arr);
        System.out.println();
        int max = s.max(arr);
        System.out.println("数组的最大值是:" + max);
        int min = s.min(arr);
        System.out.println("数组的最小值是:" + min);
        int sum = s.getsum(arr);
        System.out.println("数组的总和是:" + sum);
        int ave = s.ave(arr);
        System.out.println("数组的平均数是:" + ave);
        s.reverse(arr);
        s.print(arr);
        System.out.println("****************");
        int[] cp =s.copy(arr);
        s.print(cp);
        System.out.println("**********************");
        s.sort(arr);
        s.print(arr);
        System.out.println();
        int dex =s.index(arr,4);
        if(dex>=0){
            System.out.println("已经查找到,索引为:"+dex);
        }else{
            System.out.println("没有查找到");
        }
​
    }
​
    //    求数组的最大值
    public int max(int[] arr) {
        int m = arr[0];
        for (int i = 0; i < arr.length; i++) {
            if (arr[i] > m) {
                m = arr[i];
            }
        }
        return m;
    }
​
    //    求最小值
    public int min(int[] arr) {
        int m = arr[0];
        for (int i = 0; i < arr.length; i++) {
            if (arr[i] < m) {
                m = arr[i];
            }
        }
        return m;
    }
​
    //    求总和
    public int getsum(int[] arr) {
        int sum = 0;
        for (int i = 0; i < arr.length; i++) {
            sum += arr[i];
        }
        return sum;
    }
​
    //    求平均值
    public int ave(int[] arr) {
        return getsum(arr) / arr.length;
    }
​
    //         反转数组
    public void reverse(int[] arr) {
        for (int i = 0; i < arr.length / 2; i++) {
            int m = arr[i];
            arr[i] = arr[arr.length - i - 1];
            arr[arr.length - i - 1] =m;
        }
​
​
    }
​
    //    复制数组
    public int[] copy(int[] arr) {
        int[] cp = new int[arr.length];
        for(int i = 0;i<arr.length;i++){
            cp[i]=arr[i];
        }
        return cp;
    }
​
    //    数组排序
    public void sort(int[] arr) {
        for(int i =0 ;i<arr.length-1;i++){
            for(int j = 0;j<arr.length-1;j++){
                if(arr[j]>arr[j+1]){
                    int temp = arr[j+1];
                    arr[j+1]=arr[j];
                    arr[j]=temp;
                }
            }
        }
​
    }
​
    //    遍历数组
    public void print(int[] arr) {
        for(int i = 0;i<arr.length;i++){
            System.out.print(arr[i]+"\t");
        }
    }
​
    //    查找指定元素
    public int index(int[] arr, int dest) {
        for(int i = 0;i<arr.length;i++){
            if(dest==arr[i]){
                return i;
            }
        }
        return -1;//返回一个负数表示没有找到
    }
}
​

方法

1.方法的使用

方法的使用

public class fangfa1 {
    /*
     * 类中方法的声明和使用
     * 方法:描述类应该具有的功能
     * 比如:math类、
     * 1、举例
     * public void eat() {
     * public void sleep(int hour) {
     * public String getName() {
     * public String getNation(String nation){
     * 有无返回值 有无形参
     * 2、方法的声明:权限修饰符 返回值类型 方法名(形参列表){
     *                          方法体
     *  }
     * 3、说明
     *  3.1权限修饰符
     *  private。public、protected、缺省--->封装性
     *  3.2返回值类型:有返回值vs没有返回值
     *      3.2.1
     *      如果有返回值,必须声明时候指定返回值类型,同时必须使用return关键字返回值
     *      如果没有返回值,声明时候必须用void,不用return,除非是“return;”表示方法结束
     *      3.2.2
     *      定义方法的时候是否需要返回值
     *  3.3方法名:属于标识符,遵循标识符的命名规则
     *  3.4形参列表:方法可以声明0、1、多个
     *      数据类型1 形参1,数据类型2 形参2……
     *  4 return的作用
     *      结束方法
     *      有返回值类型,结束并且返回值
     *      return关键字后面不能有执行语句
     *  5.方法的使用:
     *          可以调用属性或者方法
     *          不可以在方法中定义方法
     * */
    public static void main(String[] args) {
        Customer p1 = new Customer();
        p1.eat();
​
    }
}
​
class Customer {
    String name;
    int age;
    boolean isMale;
​
    //  方法
    public void eat() {
        System.out.println("菲菲20220414晚上吃了米线外卖");
        return;//表示方法结束,return之后不再有语句,相当于break
    }
​
    public void sleep(int hour) {
        System.out.println("菲菲今天睡了" + hour + "小时");
    }
​
    public String getName() {
        return name;
    }
    public String getNation(String nation){
        String info = "我的粉籍是"+nation;
        return info;
​
    }
}

2.方法练习1

方法的练习一

public class fangfalianxi1 {
    public static void main(String[] args) {
        Person1 p2 = new Person1();
        p2.name="Lucy";
        p2.age=23;
        p2.sex=1;
        p2.study();
        p2.showAge();
        p2.addAge(2);
        System.out.println(p2.age);
        p2.showSex();
    }
}
 class Person1 {
    String name;
    int age;
    /**
     * sex 1表示女 2表示男
     * */
    int sex;
    public void study(){
        System.out.println("studying");
    }
    public void showAge(){
        System.out.println(age);;
    }
    public int addAge(int i){
        age+=2;
        return age;
    }
    public void showSex(){
        if(sex == 1){
            System.out.println("女性");
        }else{
            System.out.println("男性");
        }
    }
​
}

3.方法练习2

方法练习2

public class fangfalianxi2 {
    public static void main(String[] args) {
        circle e =new circle();
        e.r=3;
        double s =e.mianji();
        System.out.println(s);
        e.area();
​
    }
}
class circle{
    double r;
    public double mianji(){
        double s = Math.PI * r*r;
        return s;
    }
    public void area(){
        double s = Math.PI*r*r;
        System.out.println(s);
    }
​
​
}

4.方法练习3

方法练习3

public class fangfalianxi3 {
    public static void main(String[] args) {
        fangfalianxi3 q = new fangfalianxi3();
//        方法一
//        int i = q.method();
//        System.out.println(i);
//        方法二
        System.out.println(q.method());
    }
//    public void method(){
//        for(int i = 0;i<10;i++){
//            for(int j=0;j<8;j++){
//                System.out.print("* ");
//            }
//            System.out.println();
//        }
//    }
public int method(){
    for(int i = 0;i<10;i++){
        for(int j=0;j<8;j++){
            System.out.print("* ");
        }
        System.out.println();
    }
    return 8*10;
}
​
}

5.方法练习4

方法练习4

public class fangfalianxi4 {
    public static void main(String[] args) {
//    声明一个student数组
        Student[] stus = new Student[20];
        for (int i = 0; i < stus.length; i++) {
//            给数组元素赋值
            stus[i] = new Student();
//            给属性赋值
            stus[i].number = i + 1;
//          年级1-6
            stus[i].state = (int) (Math.random() * (6 - 1 + 1) + 1);
//            成绩0-100
            stus[i].score = (int) (Math.random() * (100 - 0 + 1) + 0);
        }
//        遍历学生对象
        for (int i = 0; i < stus.length; i++) {
            System.out.println(stus[i].info());
//            System.out.println(stus[i].state+"  "+stus[i].score+"  "+stus[i].number);
        }
//      打印出3年级学生信息
        System.out.println("--------------");
        for (int i = 0; i < stus.length; i++) {
            if (stus[i].state == 3) {
                System.out.println(stus[i].info());
            }
        }
​
//      冒泡排序并且按照成绩遍历
        for (int i = 0; i < stus.length - 1; i++) {
            for (int j = 0; j < stus.length - 1 - i; j++) {
                if (stus[j].score > stus[j + 1].score) {
//                  交换的是数组对象
                    Student temp = stus[j];
                    stus[j] = stus[j + 1];
                    stus[j + 1] = temp;
                }
            }
        }
        System.out.println("--------------");
        for (int i = 0; i < stus.length; i++) {
​
                System.out.println(stus[i].info());
​
        }
​
​
    }
​
}
​
class Student {
    int number;//学号
    int state;//年级
    int score;//成绩
​
    //    显示学生信息的方法
    public String info() {
        return "学号:" + number + "," + "年级:" + state + ",成绩:" + score;
    }
​
}

6.方法的重载

方法的重载定义及使用

public class overload {
    /*方法的重载
    1.定义:在同一个类中,允许存在一个以上的同名方法,
           只要它们的参数个数或者参数类型不同即可。
     2.”两同一不同“:同一个类,同一个方法名
                   参数列表不同,个数不同或者类型不同
     跟方法的权限修饰符、返回值类型、形参变量名、方法体都没有关系
     3.通过对象调用方法时,如何确定某一个指定的方法
     方法名--->参数列表
    * */
    public static void main(String[] args) {
        overload p = new overload();
        p.getsum(1,2);
    }
    public void getsum(String s, int i) {
        System.out.println("*****");
    }
​
    //    这两种方法为两种不同的调用
    public void getsum(int i, String s) {
        System.out.println("0000000");
    }
    public void getsum(double s, double i) {
        System.out.println("-------------");
    }
    public void getsum(int i, int s) {
        System.out.println("********");
    }
    //    public int getsum(int i ,int s){
    //        return 0;
    //    }
    //    public void getsum(int m, int n) {
    //        return 0;
    //    }
    //    private void getsum(int m, int n) {
    //
    //    }
}

  1. 方法重载的小练习

public class overloadtest {
    public static void main(String[] args) {
​
    }
    public void mOL(int i){
        System.out.println(i*i);
    }
    public void mOL(int i,int j){
        System.out.println(i*j);
    }
    public void mOL(String i){
        System.out.println(i);
    }
    public int max(int i,int j){
       return(i>j)?i:j;
    }
    public double max(double i,double j){
        return(i>j)?i:j;
    }
    public double max(double f,double g,double h) {
        double max = (f>g)?f:g;
       return  (max>h)?max:h;
    }
}

方法的重载之可变形参的定义和使用

/*
1.可变个数形参的格式:数据类型...变量名
2.当调用可变个数形参的方法时,传入的参数可以是0/1/多个
3.可变个数参数方法与本类中方法名相同,形参不同的方法构成重载
4.可变个数参数方法与本类中方法名相同,形参类型相同的数组之间不构成重载
  换句话说二者不能共存
5.可变个数形参在方法的形参中,必须声明在末尾
6.个数个数形参在方法的形参中,最多只能声明一个
* */
public class overload3 {
    public static void main(String[] args) {
        overload3 o = new overload3();
        o.show(12);
        o.show("angela");
        o.show("miss", "y");//可以有多个参数
        o.show(new String[]{"A", "B", "C", "F"});
    }
​
    public void show(int i) {
    }
​
    //    public void show(String i){
//
//    }
    public void show(String... strs) {
        System.out.println("这是可变形参");
//        仍然用数组的方法调用
        for (int i = 0; i < strs.length; i++) {
            System.out.println(strs[i]);
        }
    }
​
    //    版本更新之后不再使用这种方式,但是仍然可以用等于上述方式
//    public void show(String[] strs){
//        System.out.println("这是可变形参");
//    }
    public void show(int i, String... strs) {
    }
//  Vararg parameter must be the last in the list
//  可变个数形参必须声明在最末尾
//    public void show( String... strs,int i,) {
//    }
}

7. 递归方法

/**
 * 递归方法的使用(了解)
 * 1、递归:一个方法体内调用它自身
 */
​
public class digui1 {
    public static void main(String[] args) {
//        方法一
        int sum = 0;
        for (int i = 1; i <= 100; i++) {
            sum += i;
        }
        System.out.println(sum);
        System.out.println("-----------");
        digui1 d = new digui1();
        int sum1 = d.getsum(100);
        System.out.println(sum1);
        System.out.println("************");
        int value = d.f(10);
        System.out.println(value);
    }
​
    //   例1计算1-n之间的和
    public int getsum(int n) {
        if (n == 1) {
            return 1;
        } else {
            return n + getsum(n - 1);
        }
    }
​
    //    例2计算1-n之间的乘积 n!
    public int getcj(int n) {
        if (n == 1) {
            return 1;
        } else {
            return n * getcj(n - 1);
        }
    }
​
    //    例3已知有一个数列:f(0) = 1,f(1) = 4,
//    f(n+2)=2*f(n+1) + f(n),
//    其中n是大于0的整数,求f(10)的值。
    public int f(int n) {
        if (n == 0) {
            return 1;
        } else if (n == 1) {
            return 4;
        } else {
            return 2 * f(n - 1) + f(n - 2);
        }
    }
​
    //    例4 斐波那契数列
// 
//    计算斐波那契数列的第n个值,并且打印整个数列
​
}
​

匿名对象

匿名对象的定义和使用

/*匿名对象:没有显示的赋给一个变量名
匿名对象只能使用一次
* */
public class niming {
    public static void main(String[] args) {
        Phone p = new Phone();
        p.sendEmail();
        p.playGame();
//      匿名对象
//        new Phone().sendEmail();
//        new Phone().playGame();
        new Phone().price = 1900;
//      上下不是同一个对象,是两个匿名对象
        new Phone().showPrice();//0.0
        Phonemall mall = new Phonemall();
        mall.show(p);
//        匿名对象的使用,此时调用的是同一个对象
        System.out.println("**************");
        mall.show(new Phone());
    }
}
​
class Phonemall {
    public void show(Phone phone) {
        phone.sendEmail();
        phone.playGame();
    }
}
​
class Phone {
    double price;
​
    public void sendEmail() {
        System.out.println("发送邮件");
    }
​
    public void playGame() {
        System.out.println("玩游戏");
    }
​
    public void showPrice() {
        System.out.println("手机价格为:" + price);
    }
​
}

变量的赋值

/*变量的赋值
如果变量是基本数据类型,此时赋值的是变量所保存的数据值
如果变量是引用数据类型,此时赋值的是变量所保存的数据的地址值
* */
public class bianliangfuzhi {
    public static void main(String[] args) {
        int m = 10;
        int n = m;
        System.out.println("m="+m+",n="+n);
        n = 30;
        System.out.println("m="+m+",n="+n);
        System.out.println("******引用数据类型******");
        Order o = new Order();
        o.orderId = 888;
        Order o2 = o;
        System.out.println("o的id是"+o.orderId);
        System.out.println("o2的id是"+o2.orderId);
        o2.orderId = 1818;
//      o2指向的是和o一样的地址,所以修改o2也会改变o
//      引用类型的值不是null就是地址值
        System.out.println("o的id是"+o.orderId);//1818
        System.out.println("o2的id是"+o2.orderId);//1818
    }
​
}
class Order{
    int orderId;
}

值传递机制

/*
方法的形参的传递机制:值传递
1.形参:方法定义时,声明的小括号内的参数
  实参:方法调用时,实际传递给形参的数据
2.值传递机制
如果变量是基本数据类型,此时赋值的是变量所保存的数据值
如果变量是引用数据类型,此时赋值的是变量所保存的数据的地址值
* */
public class zhicd1 {
    public static void main(String[] args) {
        int m = 30;
        int n = 20;
        System.out.println("m=" + m + ",n=" + n);
//        交换两个变量的值的操作
//        int temp = m;
//        m=n;
//        n=temp;
        System.out.println("*************");
        zhicd1 p = new zhicd1();
        p.swap(m, n);
        System.out.println("m=" + m + ",n=" + n);
    }
//    此时交换的是形参,并没有改变m和n本身的值
    public void swap(int m, int n) {
        int temp = m;
        m = n;
        n = temp;
    }
​
}
/**
 * 此时交换成功
 */
public class zhicd2 {
    public static void main(String[] args) {
        Data data =new Data();
        data.m = 10;
        data.n = 20;
        System.out.println("m="+data.m+",n="+data.n);
//      交换m和n的值
//        int temp = data.m;
//        data.m = data.n;
//        data.n = temp;
        zhicd2 y = new zhicd2();
        y.swap(data);
        System.out.println("m="+data.m+",n="+data.n);
​
    }
//    引用类型值将data的地址给了形参
    public void swap(Data data){
        int temp = data.m;
        data.m = data.n;
        data.n = temp;
    }
​
}
class Data{
    int m;
    int n;
​
}

值传递内存解析

因为char[]的方法返回一个遍历数组

练习6

/**
 * (1)定义个circle类,包含一个double型的radius属性代表圆的半径
 * 一个findarea()方法返回圆的面积
 */
public class circle1 {
    double radius ;
    public double findArea() {
        return Math.PI * radius * radius;
    }
}
/**
 * (2)定义一个类PassObject,在类中定义一个方法printAreas()
 * 该方法的定义如下: public void printAreas(Circle c, int time)。
 * 在printAreas方法中打印输出1到time之间的每个整数半径值,
 * 以及对应的面积。例如,times为5,则输出半径1,2,3,4,5,以及对应的圆面积。
 */
​
public class passobject {
    public static void main(String[] args) {
        passobject p = new passobject();
        circle1 c = new circle1();
        p.printAreas(c,5);
        System.out.println("now radius is "+c.radius);
    }
    public void printAreas(circle1 c,int time){
        System.out.println("radius\t\tarea");
//        设置圆的半径
        for(int i =0;i<=time;i++){
            c.radius = i;
            System.out.println(c.radius+"\t\t\t"+c.findArea());
        }
        c.radius=time+1;//要求输出结果为6.0
​
    }
}

封装

/**
 * 面向对象的特征一:封装与隐藏
 * 一、问题的引入:
 * 当我们创建一个类的对象以后,我们可以通过“对象.属性”的方式
 * 对对象的属性进行赋值。这里赋值操作要受到属性的数据类型和存储范围的制约。
 * 除此之外没有其他制约条件。但是在实际问题中,我们往往需要给属性赋值
 * 加入额外的限制条件。这个条件就不能在属性声明的时候体现。
 * 我们只能通过方法进行限制条件的添加(比如:setlegs())
 * 同时我们需要避免用户再使用“对象.属性”的方式对属性进行赋值,则需要将属性声明为私有的(private)
 * 此时对属性的限制就体现了封装性
 * 二、封装性的体现
 * 我们将类的属性私有化(private),同时,将提供公共的(public)方法
 * 来获取(getlegs)和设置(setlegs)此属性的值。
 * 扩展:封装性的体现1.如上 2.不对外暴露的私有方法 3……
 * 三、封装性的体现,需要权限修饰符来配合
 * 1.java规定的4种权限从小到大(private,缺省(default),protected,public)
 * 2.4种权限可以用来修饰类及类的内部结构:属性、方法、构造器、内部类
 * 3.具体的,4种权限都可以用来修饰类的内部结构:属性、方法、构造器、内部类
 *                        修饰类只能用缺省或者public
 *  java提供4种权限修饰符来修饰类及内内部结构,体现类及类的内部结构在被调用时可见性的大小
 */
public class fengzhuang1 {
    public static void main(String[] args) {
        Animal a = new Animal();
        a.name = "cange";
        a.age = 3;
//      'legs' has private access in 'Animal'
//        a.legs = 4;
//        a.setlegs(-6);
        a.setlegs(4);
        int s =a.getlegs();
        System.out.println(s);
        a.show();
    }
}
class Animal {
    String name;
    int age;
    private int legs;//没有对外暴露,不会被修改
//    对属性的设置
    public void setlegs(int l) {
        legs = l;
        if (l >= 0 && l % 2 == 0) {
            legs = l;
        } else {
            legs = 0;
//          抛出一个异常(暂时没有讲)
        }
    }
//    对属性的获取
    public int getlegs(){
        return legs;
    }
    public void eat() {
        System.out.println("惨哥吃饭");
    }
    public void show() {
        System.out.println("name=" + name + ",age=" + age + ",legs=" + legs);
    }
}
​

封装类的测试

类内部:三种都可以使用

package fz1;
​
public class fengzhuang2 {
    private int aprivate;
    int adefault;
    public int apublic;
​
    private void methodprivate(){
        aprivate=1;
        adefault=2;
         apublic=3;
    }
    void methoddefault(){
        aprivate=1;
        adefault=2;
        apublic=3;
    }
    public void methodpublic(){
        aprivate=1;
        adefault=2;
        apublic=3;
    }
}

类外部,包内部,私有private无法使用

package fz1;
​
public class fztest {
    public static void main(String[] args) {
        fengzhuang2 o = new fengzhuang2();
        o.adefault = 1;
        o.apublic = 5;
//     'aprivate' has private access in 'fengzhuang2'
//       出了封装类就无法使用私有结构
//       o.aprivate=6;
        o.methodpublic();
        o.methoddefault();
        //      出了封装类就无法使用私有结构
//        o.methodprivate();
    }
}

包外部,private和缺省无法使用(不同包同名类中测试)

package fz2;
​
import fz1.fengzhuang2;
​
public class fztest {
​
        public static void main(String[] args) {
            fengzhuang2 o = new fengzhuang2();
//            出了类所在包,缺省和private都不能使用
//            o.adefault = 1;
            o.apublic = 5;
//     'aprivate' has private access in 'fengzhuang2'
//       出了封装类就无法使用私有结构
//       o.aprivate=6;
//            出了类所在包,缺省和private都不能使用
//            o.methoddefault();
            o.methodpublic();
​
            //  出了封装类就无法使用私有结构
//        o.methodprivate();
        }
    }

封装性练习

package fz2;
​
public class PersonTest {
    public static void main(String[] args) {
        Person p = new Person();
//        p1.age=5;
        p.setage(145);
        System.out.println("p的年龄为:"+p.getage());
    }
}
class Person{
    private int age;
    public void setage(int i){
        if(i<0||i>130){
            System.out.println("传入的数据非法!");
            return;
        }
        age = i;
    }
    public int getage(){
        return age;
    }
//    错误写法
//    public int doage(int a){
//        age=a;
//        return age;
//    }
}

构造器(类的成员三)

package gzq;
/**
 * 类的结构之三:构造器(或者构造方法、constructor)的使用
 * constructor:建设、建造
 * 一、构造器的作用:
 * 创建对象
 * 初始化对象属性
 *
 * 二、说明
 * 1.如果没有显示的定义类的构造器的话,则系统默认提供一个空参的构造器
 * 2.定义构造器的格式:权限修饰符 类名(形参列表){方法体}
 * 3.一个类中定义的多个构造器,构成重载。
 * 4. 一旦显示的定义构造器,系统就不再提供默认的空参构造器
 * 5. 一个类中至少有一个构造器
 */
public class q {
    public static void main(String[] args) {
//  创建类的对象:new + 构造器
    Person p = new Person();
    p.eat();
    Person p1 = new Person("左婧媛");
        System.out.println(p1.name);
        System.out.println(p.age);
    }
}
class Person{
    String name;
    int age;
//   默认构造器
//    public Person(){
//        System.out.println("Person()……");
//    };
    public Person(String n){
        name = n;
    }
    public Person(){
        age = 18;
    }
    public void eat(){
        System.out.println("吃艾朱瓜");
    }
}

属性赋值顺序

package gzq;
/*
* 总结,属性赋值的先后顺序
* 1.属性的默认初始化
* 2.显示初始化
* 3.构造器中赋值
* 4.通过“对象.方法”或者“对象.属性”的方式赋值
* 以上操作的先后顺序
* 1->2->3->4
* */
public class userTest {
    public static void main(String[] args) {
        User u = new User();
        System.out.println(u.age);
        User u2 = new User(2);
        u2.setAge(3);
        System.out.println(u2.age);
    }
}
class User{
    String name;
    int age=1;
    public User(){
    }
    public User(int a){
        age = a;
    }
    public void setAge(int a) {
        age = a;
    }
}

this关键字

this关键字的使用

package thistest;
​
/*
 *this关键字的使用:
 * 1.this可以用来修饰或调用:属性、方法、构造器
 * 2.this修饰属性和方法:
 *   this理解为:当前对象或者当前正在创建的对象
 *
 *   2.1在类的方法中可以使用this.属性或者方法的方式,调用当前方法
 *   通常情况下,我们都选择省略“this.”
 *   特殊情况下,如果方法的形参和类的属性同名时,必须使用this.变量,表示属性
 *   2.2在类的构造器中可以使用this.属性或者方法的方式,调用当前方法
 *   通常情况下,我们都选择省略“this.”
 *   特殊情况下,如果方法的形参和类的属性同名时,必须使用this.变量,表示属性
 * 3.this调用构造器
 * 3.1可以显示的使用“this(形参列表)”方式,调用本类中指定的其他构造器
 * 3.2构造器不能通过“this(形参列表)”方式,调用本身
 * 3.3如果一个类中有n个构造器,最多有n-1个构造器使用“this(形参列表)”
 * 3.4规定:“this(形参列表)”方式必须声明在当前构造器首行
 * 3.5构造器内部最多只能调用一个其他构造器
 * */
public class PersonTest {
    public static void main(String[] args) {
        Person p = new Person();
        p.setage(3);
        System.out.println(p.getage());
        p.eat();
        System.out.println("----------");
        Person p2 = new Person("llf",26);
        System.out.println(p2.getage());
​
    }
}
​
class Person {
    private String name;
    private int age;
    public Person(){
       this.eat();
       String info = "在Person初始化时,需要考虑如下40行代码";
        System.out.println(info);
    }
    public Person(String name){
//     调用空参构造器
        this();
        this.name=name;
    }
    public Person(int age){
          this();
        this.age=age;
//        Call to 'this()' must be first statement in constructor body
//        this();必须定义在当前构造器首行
    }
    public Person(String name,int age){
        this(age);
        this.name= name;
    }
​
    public void setname(String name) {
//        name = name;
        this.name = name;
    }
​
    public String getname() {
        return name;
    }
​
    public void setage(int age) {
//        age = age;
        this.age=age;
    }
    public int getage() {
        return age;
    }
    public void eat(){
        System.out.println("想喝咖啡");
        this.study();
    }
    public void study(){
        System.out.println("小冉好好学习");
    }
}

练习题1(结婚)

boy类

package thistest;
​
public class boy {
    private String name;
    private int age;
​
    public boy() {
​
    }
​
    public boy(String name) {
        this.name = name;
    }
​
    public boy(String name, int age) {
        this.name = name;
        this.age = age;
    }
​
    public String getName() {
        return name;
    }
​
    public void setName(String name) {
        this.name = name;
    }
​
    public int getAge() {
        return age;
    }
​
    public void setAge(int age) {
        this.age = age;
    }
​
    public void marry(Girl girl) {
        System.out.println("我想要嫁给" + girl.getName());
    }
​
    public void shout() {
        if (this.age >= 22) {
            System.out.println("你可以嫁给llf了");
        } else {
            System.out.println("你别想");
        }
    }
}

girl类

package thistest;
​
public class Girl {
    private String name;
    private int age;
    public Girl(){
​
    }
    public Girl(String name,int age){
        this.name=name;
        this.age = age;
    }
​
    public String getName() {
        return name;
    }
​
    public void setName(String name) {
        this.name = name;
    }
​
    public int getAge() {
        return age;
    }
​
    public void setAge(int age) {
        this.age = age;
    }
    public void marry(boy boy){
        System.out.println("我想嫁给llf"+boy.getName());
        boy.marry(this);
    }
/**
 * 比较两个对象的大小
 * 返回值是正数,当前对象大
 * 返回值负数,当前对象肖
 * 返回值为0,当前对象与形参对象相等
 */
    public int compare(Girl girl){
//        if(this.age>girl.age){
//            return 1;
//        }else if(this.age>girl.age){
//            return -1;
//        }else{
//            return 0;
//        }
        return this.age-girl.age;
    }
}

测试类

package thistest;
​
public class boygirl {
    public static void main(String[] args) {
        boy boy = new boy("刘力菲",26);
        boy.shout();
​
        Girl girl = new Girl("苏杉杉",25);
        girl.marry(boy);
​
        Girl girl1 = new Girl("唐莉佳",27);
        int compare = girl.compare(girl1);
        if(compare>0){
            System.out.println(girl.getName()+"年龄比较大");
        }else if(compare<0){
            System.out.println(girl1.getName()+"年龄比较大");
        }else{
            System.out.println(girl1.getName()+"和"+girl.getName()+"年龄一样大");
        }
    }
​
}
运行结果:
你可以嫁给llf了
我想嫁给llf刘力菲
我想要嫁给苏杉杉
唐莉佳年龄比较大
​

练习题2(银行)

Account类

package thistest;
​
public class Account {
    private double balance;
​
    public Account(double init_balance) {
        this.balance = init_balance;
    }
​
    public double getBalance() {
        return balance;
    }
​
    //    存款
    public void deposit(double amt) {
        if (amt > 0) {
            balance += amt;
            System.out.println("存钱成功");
        }
    }
​
    //    取款
    public void withdraw(double amt) {
        if (balance > amt) {
            balance -= amt;
            System.out.println("取钱成功");
        }else{
            System.out.println("取钱失败");
        }
    }
​
​
}

Custmoer类

package thistest;
​
public class Customer {
    private String firstName;
    private String lastName;
    private Account account;
​
    public Customer(String f, String l) {
        this.firstName = f;
        this.lastName = l;
    }
​
    public String getFirstName() {
        return firstName;
    }
​
    public String getLastName() {
        return lastName;
    }
    public void setAccount(Account account) {
        this.account = account;
    }
    public Account getAccount() {
        return account;
    }
​
​
}

bank类

package thistest;
​
public class Bank {
    private Customer[] customers;//存放多个客户
    private int numberOfCustomer;//当前客户个数
​
    public Bank(){
//        此处如果不初始化customers,它的值为null
        customers = new Customer[10];
    }
//    添加客户
    public void addCustomer(String f,String l){
        Customer cust = new Customer(f,l);
//        customers[numberOfCustomer] = cust;
//        numberOfCustomer++;
//        或
        customers[numberOfCustomer++]=cust;
    }
//   获取客户个数
    public int getNumberOfCustomer() {
        return numberOfCustomer;
    }
//    获取指定位置上的客户
    public Customer getCustomer(int index){
//        return customers[index];//可能会越界
        if(index >= 0 && index < numberOfCustomer){
            return customers[index];//return后面不再执行
        }
        return null;
    }
}

测试类

package thistest;
​
public class banktest {
    public static void main(String[] args) {
        Bank bank = new Bank();
        bank.addCustomer("李艺彤", "黄婷婷");
        bank.getCustomer(0).setAccount(new Account(800));
        bank.getCustomer(0).getAccount().withdraw(200);
        bank.getCustomer(0).getAccount().deposit(300);
        double banlance = bank.getCustomer(0).getAccount().getBalance();
        System.out.println("客户"+bank.getCustomer(0).getFirstName()+"的账户余额为:"+banlance);
​
    }
}
//运行结果
//取钱成功
//存钱成功
//客户李艺彤的账户余额为:900.0
​

package关键字

/*
* 一、package的使用
* 1.为了更好的实现项目中类的管理,提供包的概念
* 2.使用package声明类或者接口所属的包
*   声明在源文件的首行
* 3.包属于标识符,遵循标识符的命名规则(小写)
* 4.每“."一次代表一层目录
* 补充:同名包下不能重名接口、类
* */
public class packagetest {
}

MVC 设计模式

MVC是常用的设计模式之一,将整个程序分为三个层次:视图模型层,控制器层,数据模型层。这种将程序输入输出、数据处理,以及数据的展示分离开来的设计模式使程序结构变的灵活而且清晰,同时也描述了程序各个对象间的通信方式,降低了程序的耦合性。

import关键字

//import java.util.Arrays;
//import java.util.HashMap;
import java.util.*;
/*
* 二、import关键字的使用
* import 导入
* 1.在源文件中显示的使用import结构导入指定包下的类、接口
* 2.声明在包的声明和类的声明之间
* 3.需要导入多个结构,并列写出即可
* 4.可以使用“xxx.*”的方式表示导入xxx下所有结构
* 5.如果使用的类或接口是java.lang包下定义的,可以省略导包
* 6.如果使用的类或接口是本包下定义的,也可以省略
* 7.如果在代码中使用不同包下的同名的类。那么就需要使用类的全类名的方式指明调用的是哪个类。
* 8.如果已经导入java.a包下的类。那么如果需要使用a包的子包下的类的话,仍然需要导入。
* 9.import static组合的使用:调用指定类或接口下的静态的属性或方法.
* 
*/
public class packagetest {
    public static void main(String[] args) {
        String info = Arrays.toString(new int[]{1,2,3});
        HashMap map = new HashMap();
        Scanner s = null;
    }
​
​
}

面向对象中

继承性、object类

/*
 * 面向对象的特征二:继承性
 *
 * 为什么要有继承?
 *        多个类中存在相同属性和行为时,将这些内容抽取到单独一个类中,
 *        那么多个类无需再定义这些属性和行为,只要继承那个类即可。
 *  * 一、继承性的好处
 * ① 减少了代码的冗余,提高了代码的复用性;
 * ② 便于功能的扩展;
 * ③ 为之后多态性的使用,提供了前提。
 *
 * 二、继承性的格式
 *     class A extends B{}
 *  A:子类、派生类、subclass
 *  B:父类、超类、基类、superclass
 *
 *  2.1 体现:一旦子类 A 继承父类以后,子类 A 中就获取了父类 B 中声明的结构:属性、方法
 *        特别的,父类中声明为 private 的属性或方法,子类继承父类以后,仍然认为获取了父类中私有的结构。
 *        只有因为封装性的影响,使得子类不能直接调用父类的结构而已。
 *  2.2 子类继承父类以后,还可以声明自己特有的属性或方法,实现功能的拓展。
 *     子类和父类的关系:不同于子集与集合的关系。
 *     extends:延展、扩展
 * 三、Java 中关于继承性的规定:
 *      1.一个类可以被多个类继承
 *      2.Java 中类的单继承性:一个类只能有一个父类
 *      3.子父类是相对的概念。
 *      4.子类直接继承的父类,称为:直接父类。间接继承的父类,称为,间接父类。
 *      5.子类继承父类后,就获取了直接父类以及所有间接父类中声明的属性和方法。
 *
 * 四、1.如果我们没有显式的声明一个类的父类的话,则此类继承于 java.lang.Object 类
 *    2.所有的 java 类(除 java.long.Object 类之外)都直接或间接地继承于 java.lang.Object 类;
 *    3.意味着,所有的 java 类具有 java.lang.Object 类声明的功能。
 *
 */
public class jicheng {
​
    public static void main(String[] args) {
        Person p1 = new Person();
//    p1.age = 1;
        p1.eat();
        System.out.println("********************");
​
        Student s1 = new Student();
        s1.eat();
//    s1.sleep();
        s1.name = "Tom";
​
        s1.setAge(10);
        System.out.println(s1.getAge());
​
    }
}
/*
 * 为描述和处理个人信息,定义类 Person
 */
public class Person {
​
    String name;
    private int age;
​
    public Person() {
​
    }
​
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
​
    public void eat() {
        System.out.println("吃饭");
        sleep();
    }
​
    private void sleep() {
        System.out.println("睡觉");
    }
​
    public int getAge() {
        return age;
    }
​
    public void setAge(int age) {
        this.age = age;
    }
​
}
/*
 * 为描述和处理学生信息,定义类 Student
 */
public class Student extends Person {
​
    // String name;
// int age;
    String major;
​
    public Student(){
​
    }
​
    public Student(String name,int age,String major){
        this.name = name;
//    this.age = age;
        setAge(age);
        this.major = major;
    }
​
// public void eat(){
//    System.out.println("吃饭");
// }
// 
// public void sleep(){
//    System.out.println("睡觉");
// }
​
    public void study(){
        System.out.println("学习");
    }
​
    public void show(){
        System.out.println("name:" + name + ",age = " + getAge());
    }
​
}

练习

public class Mankind {
    private int sex = 1;
    private int salary = 1;
​
    public Mankind() {
​
    }
​
    public Mankind(int sex, int salary) {
        this.sex = sex;
        this.salary = salary;
    }
​
    public void manOrWoman() {
        if (sex == 1) {
            System.out.println("man");
        } else if (sex == 0) {
            System.out.println("woman");
        }
    }
​
    public void employeed() {
        if (salary == 0) {
            System.out.println("no job");
        } else if (salary == 1) {
            System.out.println("job");
        }
    }
​
    public int getSex() {
        return sex;
    }
​
    public int getSalary() {
        return salary;
    }
​
    public void setSex(int sex) {
        this.sex = sex;
    }
​
    public void setSalary(int salary) {
        this.salary = salary;
    }
}
import org.w3c.dom.ls.LSOutput;
​
public class Kids extends Mankind{
    private int yearsOld;
    public Kids(){
​
    }
​
    public Kids( int yearsOld) {
        this.yearsOld = yearsOld;
    }
​
    public void printAge(){
        System.out.println(yearsOld);
    }
​
    public int getYearsOld() {
        return yearsOld;
    }
​
    public void setYearsOld(int yearsOld) {
        this.yearsOld = yearsOld;
    }
}
public class kidstest {
    public static void main(String[] args) {
        Kids k =new Kids(12);
        k.printAge();
        k.setSalary(1);
        k.setSex(1);
        k.employeed();
        k.manOrWoman();
​
    }
}

方法的重写

/**
 * 方法的重写(override/overwrite)
 * 1、重写:子类继承父类以后,可以对父类中同名参数的方法,进行覆盖操作
 * 2、应用:当重写以后,当创建子类对象以后,通过子类对象调用父类中
 *        的同名同参数的方法时实际执行的是重写的方法
 * 3、重写的规定:
 * 方法的声明:权限修饰符 返回值类型 方法名(形参列表)throws 异常的类型{
 *                                  方法体
 * }
 * 约定:子类中叫重写的方法
 *      父类中叫被重写的方法
 * 1 、    子类重写的方法的方法名和形参列表与父类相同
 * 2 、    子类重写的权限修饰符不小于父类的
 * 特殊情况:子类不能重写父类中为private权限的方法
 * 3、返回值类型:
 * 父类 void                子类 void
 * 父类 A类型               子类 A类或A的子类
 * 父类 基本数据类型(double)子类 必须是相同基本数据类型(double)
 * 4、子类重写的方法抛出的异常不大于父类被重写的方法抛出的异常
 * 子类和父类中的同名同参数的方法要么都声明为static,要么非static
 */
​
public class cxtest {
    public static void main(String[] args) {
        Student2  s = new Student2();
        s.eat();
        s.walk(10);
        System.out.println("*************");
        s.study();
​
    }
​
}
​
public class Person2 {
​
    String name;
    int age;
​
    public Person2() {
​
    }
​
    public Person2(String name, int age) {
        this.name = name;
        this.age = age;
    }
​
    static void eat() {
        System.out.println("吃饭");
    }
​
​
    public void walk(int distance) {
        System.out.println("走路,走的距离是:" + distance + "公里");
        show();
    }
​
    private void show() {
        System.out.println("我是一个人");
    }
​
    public Object info() {
        return null;
    }
    public double info1() {
        return 1.0;
    }
​
​
}
​
public class Student2 extends Person2 {
​
    String major;
​
    public Student2() {
​
    }
​
    public Student2(String major) {
        this.major = major;
    }
​
    public void study() {
        System.out.println("学习,专业是:" + major);
    }
​
    //    对父类中的eat()进行了重写
//     void eat(){//权限修饰符不能小于父类此方法
    static void eat() {
        System.out.println("学生应该多吃有营养的食物");
    }
​
    //子类不能重写父类中声明为private的方法
    public void show() {
        System.out.println("我是一个戆卵");
    }
​
    public String info() {
        return null;
    }
​
    //    public int info1() {基本数据类型必须一致
    public double info1() {
        return 1.0;
    }
​
    @Override//重写标记
    public void walk(int distance) {
        super.walk(distance);
    }
}
​

四种权限修饰符

super关键字

public class Person {
        String name;
        int age;
        int id=99;//身份证号码
        public Person(){
            System.out.println("超绝可爱刘力菲");
​
        }
        public Person(String name){
            this.name = name;
        }
        public Person(String name,int age){
            this(name);
            this.age = age;
        }
        public void eat(){
            System.out.println("人,吃饭");
        }
        public void walk(){
            System.out.println("人,走路");
        }
    }
public class Student extends Person{
        String major;
        int id=88;//学号 属性不会覆盖
        public Student(){
        //调用了父类的空参构造器
        }
        public Student(String major){
            this.major = major;
        }
        public Student(String name,int age,String major){
            super(name,age);
            this.major = major;
    }
        public void eat(){
            System.out.println("学生多吃有营养的食物");
        }
​
        public void study(){
            System.out.println("学生,学习知识。");
            this.eat();
            super.eat();
        }
        public void show(){
            System.out.println("name="+name+",age="+age);
            System.out.println("id="+id);
            System.out.println("id="+super.id);
        }
}
/*
* super关键字的使用
 * 1.super理解为:父类的
 * 2.super可以用来调用:属性、方法、构造器
 *
 * 3.super的使用
 *        3.1 我们可以在子类的方法或构造器中,通过"super.属性"或"super.方法"的方式,显式的调用
 *     父类中声明的属性或方法。但是,通常情况下,我们习惯去省略这个"super."
 *        3.2 特殊情况:当子类和父类中定义了同名的属性时,我们要想在子类中调用父类中声明的属性,则必须显式的
 *  使用"super.属性"的方式,表明调用的是父类中声明的属性。
 *     3.3 特殊情况:当子类重写了父类中的方法后,我们想在子类的方法中调用父类中被重写的方法时,必须显式的
 *  使用"super.方法"的方式,表明调用的是父类中被重写的方法。
 *
 * 4.super调用构造器
 *       4.1  我们可以在子类的构造器中显式的使用"super(形参列表)"的方式,调用父类中声明的指定的构造器
 *       4.2 "super(形参列表)"的使用,必须声明在子类构造器的首行!
 *    4.3 我们在类的构造器中,针对于"this(形参列表)"或"super(形参列表)"只能二选一,不能同时出现。
 *    4.4 在构造器的首行,既没有显式的声明"this(形参列表)"或"super(形参列表)",则默认的调用的是父类中的空参构造器。super()
 *    4.5 在类的多个构造器中,至少有一个类的构造器使用了"super(形参列表)",调用父类中的构造器。
 *
* */
public class sp {
    public static void main(String[] args) {
        Student s = new Student();
        s.show();
        System.out.println("***************");
        s.eat();
        s.study();
        Student s2 = new Student("Tom",18,"语文");
        s2.show();
        System.out.println("***************");
        Student s3 = new Student();
    }
}

子类对象实例化

/*
 * 子类对象实例化的全过程
 * 
 * 1.从结果上看:
 * 		子类继承父类以后,就获取了父类中声明的属性或方法。
 * 		创建子类的对象中,在堆空间中,就会加载所有父类中声明的属性。
 * 
 * 2.从过程上看:
 * 		当我们通过子类的构造器创建子类对象时,我们一定会直接或间接的调用其父类构造器, 
 * 		直到调用了java.lang.Object类中空参的构造器为止。正因为加载过所有的父类结构,所以才可以看到内存中有
 * 		父类中的结构,子类对象可以考虑进行调用。
 * 
 * 明确:虽然创建子类对象时,调用了父类的构造器,但自始至终就创建过一个对象,即为new的子类对象。
 */
public class InstanceTest {
}



多态性

面向对象之三:多态性

1.理解多态性:可以理解为一个事物的多种态性

2.何为多态性:

对象的多态性:父类的引用指向子类的对象(或子类的对象赋值给父类的引用)

3.多态的使用:虚拟方法调用

有了对象多态性以后,我们在编译期,只能调用父类声明的方法,但在执行期实际执行的是子类重写的方法。

若编译时类型和运行时类型不一致,就出现了对象的多态性

“看左边”:看的是父类的引用(父类中不具备子类特有的方法)

“看右边”:看得是子类的对象(实际运行的是子类重写父类的方法)

4多态性的使用前提

类的继承关系 方法的重写

5.对象的多态性:只适用于方法,不适用于属性(编译和运行都看左边)

面向对象下

static关键字

static的使用

当我们编写一个类时,其实就是在描述其对象的属性和行为,而并没有产生实质上的对象,只有通过 new 关键字才会产生出对象,这时系统才会分配内存空间给对象,其方法才可以供外部调用。

我们有时候希望无论是否产生了对象或无论产生了多少对象的情况下,某些特定的数据在内存空间里只有一份。

例如所有的中国人都有个国家名称,每一个中国人都共享这个国家名称,不必在每一个中国人的实例对象中都单独分配一个用于代表国家名称的变量。

/*
 * static 关键字的使用
 * 
 * 1.static:静态的。
 * 2.static 可以用来修饰:属性、方法、代码块、内部类。
 * 
 * 3.使用 static 修饰属性:静态变量(或类变量)。
 *      3.1  属性:是否使用 static 修饰,又分为:静态属性 VS 非静态属性(实例变量)
 *         实例变量:我们创建了类的多个对象,每个对象都独立的拥有了一套类中的非静态属性。
 *              当修改其中一个非静态属性时,不会导致其他对象中同样的属性值的修饰。
 *         静态变量:我们创建了类的多个对象,多个对象共享同一个静态变量。当通过静态变量去修改某一个变量时,
 *              会导致其他对象调用此静态变量时,是修改过的。
 *      3.2 static 修饰属性的其他说明:
 *          ① 静态变量随着类的加载而加载。可以通过"类.静态变量"的方式进行调用。
 *          ② 静态变量的加载要早于对象的创建。
 *          ③ 由于类只会加载一次,则静态变量在内存中也只会存在一次。存在方法区的静态域中。
 * 
 *          ④       类变量     实例变量
 *          类       yes         no
 *          对象      yes         yes
 * 
 *      3.3 静态属性举例:System.out.Math.PI;
 *  
 */
public class StaticTest {
    public static void main(String[] args) {
        
        Chinese.nation = "中国";
        
        Chinese c1 = new Chinese();
        c1.name = "姚明";
        c1.age = 40;
        c1.nation = "CHN";
        
        Chinese c2 = new Chinese();
        c2.name = "马龙";
        c2.age = 30;
        c2.nation = "CHINA";
        
        System.out.println(c1.nation); 
        
        //编译不通过
//      Chinese.name = "张继科";
        
    }
}
//中国人
class Chinese{
    
    String name;
    int age;
    static String nation;
}
​

类变量vs实例变量内存解析

static修饰方法

/* 
 * 4.使用 static 修饰方法:静态方法
 *      ① 随着类的加载而加载,可以通过"类.静态方法"的方式调用
 *      ②           静态方法        非静态方法
 *          类       yes         no
 *          对象      yes         yes
 *      ③ 静态方法中,只能调用静态的方法或属性
 *        非静态的方法中,可以调用所有的方法或属性
 * 
 * 5.static 注意点:
 *   5.1  在静态的方法内,不能使用 this 关键字、super 关键字
 *   5.2 关于静态属性和静态方法的使用,大家从生命周期的角度去理解。
 *   
 * 6.开发中,如何确定一个属性是否需要声明 static 的?
 *   》 属性是可以被多个对象所共享的,不会随着对象的不同而不同的。
 *   》 类中的常量也常常声明为 static
 *   
 *   开发中,如何确定一个方法是否要声明为 static 的?
 *   》 操作静态属性的方法,通常设置为 static 的
 *   》 工具类中的方法,习惯上声明为 static 的。比如:Math、Arrays、Collections
 *   
 */
public class StaticTest {
    public static void main(String[] args) {
        
        Chinese.nation = "中国";
        
        Chinese c1 = new Chinese();
        
        //编译不通过
//      Chinese.name = "张继科";
        
        c1.eat();
        
        Chinese.show();
        //编译不通过
//      chinese.eat();
//      Chinese.info();
    }
}
//中国人
class Chinese{
    
    String name;
    int age;
    static String nation;
    
    public void eat(){
        System.out.println("中国人吃中餐");
        //调用非静态结构
        this.info();
        System.out.println("name : " + name);
        //调用静态结构
        walk();
        System.out.println("nation : " + Chinese.nation);
    }
    
    public static void show(){
        System.out.println("我是一个中国人!");
//      eat();
//      name = "Tom";
        //可以调用静态的结构
        System.out.println(Chinese.nation);
        walk();
    }
    
    public void info(){
        System.out.println("name : " + name + ",age : " + age);
    }
    
    public static void walk(){
        
    }
}
​

static的应用举例

//static 关键字的应用
public class CircleTest {
    public static void main(String[] args) {
        
        Circle c1 = new Circle();
        
        Circle c2 = new Circle();
        
        Circle c3 = new Circle();
        
        System.out.println("c1 的 ID:" + c1.getId());
        System.out.println("c2 的 ID:" + c2.getId());
        System.out.println("c3 的 ID:" + c3.getId());
        
        System.out.println("创建圆的个数: " + Circle.getTotal());
        
    }
    
}
​
class Circle{
    
    private double radius;
    private int id; //需要自动赋值
    
    public Circle(){
        id = init++;
        total++;
    }
    
    public Circle(double radius){
        this();
        //或
//      id = init++;
//      total++;
        this.radius = radius;
    }
    
    private static int total;//记录创建圆的个数
    private static int init = 1001;//static 声明的属性被所有对象所共享
    
    public double findArea(){
        return 3.14 * radius * radius;
    }
​
    public double getRadius() {
        return radius;
    }
​
    public void setRadius(double radius) {
        this.radius = radius;
    }
​
    public int getId() {
        return id;
    }
​
    public static int getTotal() {
        return total;
    }
    
}
​

单例设计模式

设计模式是在大量的实践中总结和理论化之后优选的代码结构、编程风格、以及解决问题的思考方式。设计模免去我们自己再思考和摸索。就像是经典的棋谱,不同的棋局,我们用不同的棋谱。”套路”

所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例。并且该类只提供一个取得其对象实例的方法。如果我们要让类在一个虚拟机中只能产生一个对象,我们首先必须将类的构造器的访问权限设置为 private,这样,就不能用 new 操作符在类的外部产生类的对象了,但在类内部仍可以产生该类的对象。因为在类的外部开始还无法得到类的对象,只能调用该类的某个静态方法以返回类内部创建的对象,静态方法只能访问类中的静态成员变量,所以,指向类内部产生的该类对象的变量也必须定义成静态的。

饿汉式

/*
 * 单例设计模式:
 * 1.所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例
 *  
 * 2.如何实现?
 *   饿汉式    VS  懒汉式
 * 
 * 3.区分饿汉式和懒汉式。
 *     饿汉式:坏处:对象加载时间过长。
 *             好处:饿汉式是线程安全的。
 * 
 *   懒汉式:好处:延迟对象的创建。
 *             坏处:目前的写法,会线程不安全。---》到多线程内容时,再修改
 */
public class SingletonTest {
    public static void main(String[] args) {
//      Bank bank1 = new Bank(); 
//      Bank bank2 = new Bank(); 
        
        Bank bank1 = Bank.getInstance();
        Bank bank2 = Bank.getInstance();
        
        System.out.println(bank1 == bank2);
        
    }
}
​
//单例的饿汉式
class Bank{
    
    //1.私有化类的构造器
    private Bank(){
        
    }
    
    //2.内部创见类的对象
    //4.要求此对象也必须声明为静态的
    private static Bank instance = new Bank();
    
    //3.提供公共的静态的方法,返回类的对象。
    public static Bank getInstance(){
        return instance;
    }
}
​

单例的懒汉式

/*
 * 单例的懒汉式实现
 * 
 */
public class SingletonTest2 {
    public static void main(String[] args) {
        
        Order order1 = Order.getInstance();
        Order order2 = Order.getInstance();
        
        System.out.println(order1 == order2);
    }
}
class Order{
    //1.私有化类的构造器
    private Order(){
        
    }
    
    //2.声明当前类对象,没有初始化。
    //此对象也必须声明为 static 的
    private static Order instance = null;
    
    //3.声明 public、static 的返回当前类对象的方法
    public static Order getInstance(){
        if(instance == null){
            instance = new Order();         
        }
        return instance;
    }
}
​

由于单例模式只生成一个实例,减少了系统性能开销,当一个对象的产生需要比较多的资源时,如读取配置、产生其他依赖对象时,则可以通过在应用启动时直接产生一个单例对象,然后永久驻留内存的方式来解决。

简单理解main方法

由于 Java 虚拟机需要调用类的 main()方法,所以该方法的访问权限必须是 public,又因为 Java 虚拟机在执行 main()方法时不必创建对象,所以该方法必须是 static 的,该方法接收一个 String 类型的数组参数,该数组中保存执行 Java 命令时传递给所运行的类的参数。

又因为 main() 方法是静态的,我们不能直接访问该类中的非静态成员,必须创建该类的一个实例对象后,才能通过这个对象去访问类中的非静态成员,这种情况,我们在之前的例子中多次碰到。

/*
 * main()方法的使用说明
 * 1.main()方法作为程序的入口;
 * 2.main()方法也是一个普通的静态方法
 * 3.main()方法也可以作为我们与控制台交互的方式。(之前,使用 Scanner)
 * 
 * 
 */
public class MainTest {
    public static void main(String[] args) {    //入口
        
        Main.main(new String[100]);
        
        MainTest test = new MainTest();
        test.show();
    }
    
    public void show(){
        
    }
}
​
class Main{
    public static void main(String[] args) {
        args = new String[100];
        for(int i = 0;i < args.length;i++){
            args[i] = "args_" + i;
            System.out.println(args[i]);
        }
    }
}
​
public class MainDemo {
    public static void main(String[] args) {
        
        for(int i = 0;i < args.length;i++){
            System.out.println("/*/*/*/"+ args[i]);
        }   
    }
}
​

类的成员之四:代码块

/*
 * 类的成员之四:代码块(或初始化块)
 * 
 * 1.代码块的作用:用来初始化类、对象的
 * 2.代码块如果有修饰的话,只能使用 static
 * 3.分类:静态代码块 vs 非静态代码块
 * 
 * 4.静态代码块
 *  》内部可以有输出语句
 *  》随着类的加载而执行,而且只执行一次
 *  》作用:初始化类的信息
 *  》如果一个类中,定义了多个静态代码块,则按照声明的先后顺序执行
 *  》静态代码块的执行,优先于非静态代码块的执行
 *  》静态代码块内只能调用静态的属性、静态的方法,不能调用非静态的结构
 * 
 * 5.非静态代码块
 *  >内部可以有输出语句
 *  >随着对象的创建而执行
 *  >每创建一个对象,就执行一次非静态代码块。
 *  >作用:可以在创建对象时,对对象的属性等进行初始化。
 *  >如果一个类中,定义了多个非静态代码块,则按照声明的先后顺序执行
 *  >非静态代码块内可以调用静态的属性、静态的方法,或非静态的属性、非静态的方法。
 *  
 * 对属性可以赋值的位置:
 *  ①默认初始化
 *  ②显式初始化
 *  ③构造器中初始化
 *  ④有了对象以后,可以通过"对象.属性"或"对象.方法"的方式,进行赋值。
 *  ⑤在代码块中赋值
 */
public class BlockTest {
    public static void main(String[] args) {
        
        String desc = Person.desc;
        System.out.println(desc);
        
        Person p1 = new Person();
        Person p2 = new Person();
        System.out.println(p1.age);
        
        Person.info();
    }
}
​
class Person{
    //属性
    String name;
    int age;
    static String desc = "我是一个青年";
    
    //构造器
    public Person(){
        
    }
    
    //static 的代码块
    static{
        System.out.println("hello,static block-1");
        //调用静态结构
        desc = "我是一个爱小说的人";
        info();
        //不能调用非静态结构
//      eat();
//      name = "Tom";
    }
    
    static{
        System.out.println("hello,static block-2");
    }
    
    //非 static 的代码块
    {
        System.out.println("hello,block-2");
    }
    {
        System.out.println("hello,block-1");
        //调用非静态结构
        age = 1;
        eat();
        //调用静态结构
        desc = "我是一个爱小说的人 1";
        info();
    }   
    
    //方法
    public Person(String name,int age){
        this.name = name;
        this.age = age;
    }
    
    public void eat(){
        System.out.println("吃饭");
    }
​
    @Override
    public String toString() {
        return "Person [name=" + name + ", age=" + age + "]";
    }
    public static void info(){
        System.out.println("我是一个快乐的人。");
    }
    
}
​
//总结:由父类到子类,静态先行
class Root{
    static{
        System.out.println("Root 的静态初始化块");
    }
    {
        System.out.println("Root 的普通初始化块");
    }
    public Root(){
        System.out.println("Root 的无参数的构造器");
    }
}
class Mid extends Root{
    static{
        System.out.println("Mid 的静态初始化块");
    }
    {
        System.out.println("Mid 的普通初始化块");
    }
    public Mid(){
        System.out.println("Mid 的无参数的构造器");
    }
    public Mid(String msg){
        //通过 this 调用同一类中重载的构造器
        this();
        System.out.println("Mid 的带参数构造器,其参数值:"
            + msg);
    }
}
class Leaf extends Mid{
    static{
        System.out.println("Leaf 的静态初始化块");
    }
    {
        System.out.println("Leaf 的普通初始化块");
    }   
    public Leaf(){
        //通过 super 调用父类中有一个字符串参数的构造器
        super("尚硅谷");
        System.out.println("Leaf 的构造器");
    }
}
public class LeafTest{
    public static void main(String[] args){
        new Leaf(); 
        //new Leaf();
    }
}
​
class Father {
    static {
        System.out.println("11111111111");
    }
    {
        System.out.println("22222222222");
    }
​
    public Father() {
        System.out.println("33333333333");
​
    }
​
}
​
public class Son extends Father {
    static {
        System.out.println("44444444444");
    }
    {
        System.out.println("55555555555");
    }
    public Son() {
        System.out.println("66666666666");
    }
​
    public static void main(String[] args) { // 由父及子 静态先行
        System.out.println("77777777777");
        System.out.println("************************");
        new Son();
        System.out.println("************************");
​
        new Son();
        System.out.println("************************");
        new Father();
    }
​
}
​

/*
 * 对属性可以赋值的位置:
 *  ①默认初始化
 *  ②显式初始化 / ⑤在代码块中赋值 看谁在前谁就先执行,谁在后谁就后执行。
 *  ③构造器中初始化
 *  ④有了对象以后,可以通过"对象.属性"或"对象.方法"的方式,进行赋值。
 *  
 *  执行的先后顺序:① - ② / ⑤ - ③ - ④
 */
public class OrderTest {
    public static void main(String[] args) {
        Order order = new Order();
        System.out.println(order.orderId);
    }
}
class Order{
    
    int orderId = 3;
    {
        orderId = 4;
    }
    
}
​

关键字:final

/*
 * final:最终的
 * 
 * 1.final可以用来修饰的结构:类、方法、变量
 * 
 * 2.final用来修饰一个类:此类不能被其他类所继承。
 *        比如:String类、System类、StringBuffer类
 * 3.final修饰一个方法:final标记的方法不能被子类重写。
 *        比如:Object类中的getClass()。 
 * 4.final用来修饰变量:此时的"变量"(成员变量或局部变量)就是一个常量。名称大写,且只能被赋值一次。
 *   4.1 final修饰属性,可以考虑赋值的位置有:显式初始化、代码块中初始化、构造器中初始化
 *   4.2 final修饰局部变量:
 *       尤其是使用final修饰形参时,表明此形参是一个常量。当我们调用此方法时,给常量形参赋一个实参。
 *      一旦赋值以后,就只能在方法体内使用此形参,但不能进行重新赋值。
 *      
 * static final 用来修饰:全局常量
 */
public class FinalTest {
    
    final int WIDTH = 0;
    final int LEFT;
    final int RIGHT;
//  final int DOWN;
    
    {
        LEFT = 1;
    }
    
    public FinalTest(){
        RIGHT = 2;
    }
    
    public FinalTest(int n){
        RIGHT = n;
    }
    
//  public void setDown(int down){
//      this.DOWN = down;
//  }
    
    public void dowidth(){
//      width = 20; //width cannot be resolved to a variable
    }
    
    public void show(){
        final int NUM = 10; //常量
//      num += 20;
    }
    
    public void show(final int num){
        System.out.println(num);
    }
    
    public static void main(String[] args) {
        
        int num = 10;
        
        num = num + 5;
        
        FinalTest test = new FinalTest();
//      test.setDown(5);
        
        test.show(10);
    }
}
​
final class FianlA{
    
}
​
//class B extends FinalA{     //错误,不能被继承。
//  
//}
​
//class C extends String{
//  
//}
​
class AA{
    public final void show(){
        
    }
}
​
//class BB extends AA{  // 错误,不能被重写。
//  public void show(){
//      
//  }
//}
​

抽象类与抽象方法

/*
 * abstract 关键字的使用
 * abstract 
 * 1.abstract:抽象的
 * 2.abstract 可以用来修饰的结构:类、方法
 * 3.abstract 修饰类:抽象类
 *  》 此类不能实例化
 *  》 抽象类中一定有构造器,便于子类实例化时调用(涉及:子类对象实例化全过程)
 *  》 开发中,都会提供抽象类的子类,让子类对象实例化,实现相关的操作
 * 
 * 4.abstract 修饰方法:抽象方法
 *  > 抽象方法,只有方法的声明,没有方法体。
 *  > 包含抽象方法的类,一定是一个抽象类。反之,抽象类中可以没有抽象方法
 *  > 若子类重写了父类中所有的抽象方法,此子类,
 *
 * abstract 使用上的注意点:
 * 1.abstract 不能用来修饰变量、代码块、构造器;
 * 
 * 2.abstract 不能用来修饰私有方法、静态方法、final 的方法、final 的类。
 * 
 */
public class AbstractTest {
    public static void main(String[] args) {
        //一旦 Person 类抽象了,就不可实例化
//      Person p1 = new Person();
//      p1.eat();
        
    }
}
​
abstract class Creature{
    public abstract void breath();
}
​
abstract class Person extends Creature{
    String name;
    int age;
    
    public Person(){
        
    }
    
    public Person(String name,int age){
        this.name = name;
        this.age = age;
    }
    
    //不是抽象方法
//  public void eat(){
//      System.out.println("人吃饭");
//  }
    
    //抽象方法
    public abstract void eat();
    
    public void walk(){
        System.out.println("人走路");
    }
}
​
class Student extends Person{
    public Student(String name,int age){
        super(name,age);
    }
    public void eat(){
        System.out.println("学生应该多吃有营养的。");
    }
    @Override
    public void breath() {
        System.out.println("学生应该呼吸新鲜的无雾霾空气");
    }
}
​

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

/* Java 允许类设计者指定:超类声明一个方法但不提供实现,该方法的实现由子类提  供。这样的方法称为抽象方法。有一个或更多抽象方法的类称为抽象类。
 * Vehicle 是一个抽象类,有两个抽象方法。
 * 注意:抽象类不能实例化 new Vihicle()是非法的
 */
public abstract class Vehicle{
    public abstract double calcFuelEfficiency();//计算燃料效率的抽象方法
    public abstract double calcTripDistance();//计算行驶距离的抽象方法
}
public class Truck extends Vehicle{
    public double calcFuelEfficiency(){ 
        //写出计算卡车的燃料效率的具体方法
    }
    public double calcTripDistance(){ 
        //写出计算卡车行驶距离的具体方法
    }
}
public class RiverBarge extends Vehicle{
    public double calcFuelEfficiency() { 
        //写出计算驳船的燃料效率的具体方法
    }
    public double calcTripDistance( )  {  
        //写出计算驳船行驶距离的具体方法
    }
}
​

/*
 * 编写一个 Employee 类,声明为抽象类,
 * 包含如下三个属性:name,id,salary。
 * 提供必要的构造器和抽象方法:work()。
 * 对于 Manager 类来说,他既是员工,还具有奖金(bonus)的属性。
 * 请使用继承的思想,设计 CommonEmployee 类和 Manager 类,
 * 要求类中提供必要的方法进行属性访问。
 * 
 */
public abstract class Employee {
    
    private String name;
    private int id;
    private double salary;
    
    public Employee(){
        super();
    }
​
    public Employee(String name, int id, double salary) {
        super();
        this.name = name;
        this.id = id;
        this.salary = salary;
    }
    
    public abstract void work();    
}
​
/*
 * 对于 Manager 类来说,他既是员工,还具有奖金(bonus)的属性。
 * 
 */
public class Manager extends Employee{
​
    private double bonus;   //奖金
    
    public Manager(double bonus) {
        super();
        this.bonus = bonus;
    }
    
    public Manager(String name, int id, double salary, double bonus) {
        super(name, id, salary);
        this.bonus = bonus;
    }
​
​
    @Override
    public void work() {
        System.out.println("管理员工,提高公司运行效率。");       
    }
}
​
/*
 * 请使用继承的思想,设计 CommonEmployee 类和 Manager 类,
 */
public class EmployeeTest {
    public static void main(String[] args) {
        
        Employee manager = new Manager("库克",1001,5000,50000);//多态
        
        manager.work();
        
        CommonEmployee commonEmployee = new CommonEmployee();
        commonEmployee.work();
    }
}
​

创建抽象类的匿名子类对象

public class Num {
​
}
​
abstract class Creature{
    public abstract void breath();
}
​
abstract class Person extends Creature{
    String name;
    int age;
    
    public Person(){
        
    }
    
    public Person(String name,int age){
        this.name = name;
        this.age = age;
    }
    
    //不是抽象方法
//  public void eat(){
//      System.out.println("人吃饭");
//  }
    
    //抽象方法
    public abstract void eat();
    
    public void walk(){
        System.out.println("人走路");
    }
}
​
class Student extends Person{
    public Student(String name,int age){
        super(name,age);
    }
    public Student(){
​
    }
    public void eat(){
        System.out.println("学生应该多吃有营养的。");
    }
    @Override
    public void breath() {
        System.out.println("学生应该呼吸新鲜的无雾霾空气");
    }
}
​
/*
 * 抽象类的匿名子类
 * 
 */
public class PersonTest {
    public static void main(String[] args) {
        
        method(new Student());  //匿名对象
        
        Worker worker = new Worker(); 
        method1(worker);    //非匿名的类非匿名的对象
        
        method1(new Worker());  //非匿名的类匿名的对象
        
        System.out.println("*********************");
        
        //创建了一个匿名子类的对象:p
        Person p = new Person(){
​
            @Override
            public void eat() {
                System.out.println("吃东西");
            }
​
            @Override
            public void breath() {
                System.out.println("呼吸空气");
            }
            
        };
        method1(p);
        System.out.println("**********************"); 
        //创建匿名子类的匿名对象
        method1(new Person(){
​
            @Override
            public void eat() {
                System.out.println("吃零食");
            }
​
            @Override
            public void breath() {
                System.out.println("云南的空气");
            }
            
        });
    }
    
    public static void method1(Person p){
        p.eat();
        p.walk();
    }
    
    public static void method(Student s){
        
    }
}
class Worker extends Person{
    
    @Override
    public void eat() {
    }
​
    @Override
    public void breath() {
    }
}
​

抽象类的练习八:面向对象(下)_卖剩鸭的博客-CSDN博客

接口

一方面,有时必须从几个类中派生出一个子类,继承它们所有的属性和方法。但是,Java 不支持多重继承。有了接口,就可以得到多重继承的效果。

另一方面,有时必须从几个类中抽取出一些共同的行为特征,而它们之间又没有 is-a 的关系,仅仅是具有相同的行为特征而已。例如:鼠标、键盘、打印机、扫描仪、摄像头、充电器、MP3 机、手机、数码相机、移动硬盘等都支持 USB 连接。

接口就是规范,定义的是一组规则,体现了现实世界中“如果你是/要…则必须能…”的思想。继承是一个"是不是"的关系,而接口实现则是"能不能"的关系。

接口的本质是契约,标准,规范,就像我们的法律一样。制定好后大家都要遵守

  • /* 接口(interface)是抽象方法和常量值定义的集合。
    ​
    - 接口的特点:
    - 用 interface 来定义。
    - 接口中的所有成员变量都默认是由 public static final 修饰的。
    - 接口中的所有抽象方法都默认是由 public abstract 修饰的。
    - 接口中没有构造器。
    - 接口采用多继承机制。
      */·
      
/*
 * 接口的使用
 * 1.接口使用 interface 来定义。
 * 2.在 Java 中:接口和类是并列的两个结构
 * 3.如何去定义两个接口:定义接口中的成员
 *  》3.1 JDK7 及以前:只能定义全局常量和抽象方法
 *      》全局常量:public static final 的,但是书写中,可以省略不写。
 *      》抽象方法:public abstract 的
 * 
 *  》3.2 JDK8:除了全局常量和抽象方法之外,还可以定义静态方法、默认方法(略)。
 * 
 * 4.接口中不能定义构造器!意味着接口不可以实例化。
 * 
 * 5.Java 开发中,接口通过让类去实现(implements)的方式来使用。
 *   如果实现类覆盖了接口中的所有方法,则此实现类就可以实例化
 *   如果实现类没有覆盖接口中所有的抽象方法,则此实现类仍为一个抽象类
 * 
 * 6.Java 类可以实现多个接口 ---》弥补了 Java 单继承性的局限性
 *  格式:class AA extends BB implementd CC,DD,EE
 *  
 *  7.接口与接口之间是继承,而且可以多继承
 *  
 **********************************
 * 8.接口的具体使用,体现多态性
 *     接口的主要用途就是被实现类实现。(面向接口编程)
 * 9.接口,实际可以看作是一种规范
 * 
 * 面试题:抽象类与接口有哪些异同?
 *  
 */
public class InterfaceTest {
    public static void main(String[] args) {
        System.out.println(Flayable.MAX_SPEED);
        System.out.println(Flayable.MIN_SPEED);
    }
}
interface Flayable{
    
    //全局变量
    public static final int MAX_SPEED = 7900;   
    int MIN_SPEED = 1;//省略了 public static final 
    
    //抽象方法
    public abstract void fly();
    
    void stop();//省略了 public abstract 
    //Interfaces cannot have constructors
//  public Flayable(){
//      
//  }   
}
interface Attackable{
    void attack();
}
​
class Plane implements Flayable{
​
    @Override
    public void fly() {
        System.out.println("飞机通过引擎起飞");
        
    }
​
    @Override
    public void stop() {
        System.out.println("驾驶员减速停止");
    }
    
}
abstract class Kite implements Flayable{
​
    @Override
    public void fly() {
        
    }
}
​
class Bullet extends Object implements Flayable,Attackable,CC{
​
    @Override
    public void attack() {
        // TODO Auto-generated method stub
        
    }
​
    @Override
    public void fly() {
        // TODO Auto-generated method stub
        
    }
​
    @Override
    public void stop() {
        // TODO Auto-generated method stub
        
    }
​
    @Override
    public void method1() {
        // TODO Auto-generated method stub
        
    }
​
    @Override
    public void method2() {
        // TODO Auto-generated method stub
        
    }
}
​
//*********************************
interface AA{
    void method1();
}
interface BB{
    void method2();
}
interface CC extends AA,BB{
    
}
​

/*
 * 接口的使用
 * 1.接口使用上也满足多态性
 * 2.接口,实际上就是定义了一种规范
 * 3.开发中,体会面向接口编程!
 * 
 */
public class USBTest {
    public static void main(String[] args) {
        
        Computer com = new Computer();
        //1.创建了接口的非匿名实现类的非匿名对象
        Flash flash = new Flash();
        com.transferData(flash); 
        //2. 创建了接口的非匿名实现类的匿名对象
        com.transferData(new Printer());
        //3. 创建了接口的匿名实现类的非匿名对象
        USB phone = new USB(){
​
            @Override
            public void start() {
                System.out.println("手机开始工作");
            }
​
            @Override
            public void stop() {
                System.out.println("手机结束工作");
            }
            
        };
        com.transferData(phone);
        //4. 创建了接口的匿名实现类的匿名对象
        com.transferData(new USB(){
            @Override
            public void start() {
                System.out.println("mp3 开始工作");
            }
​
            @Override
            public void stop() {
                System.out.println("mp3 结束工作");
            }
        });
    }
}
​
class Computer{
    
    public void transferData(USB usb){//USB usb = new Flash();
        usb.start();
        
        System.out.println("具体传输数据的细节");
        
        usb.stop();
    }
    
}
​
interface USB{
    //常量:定义了长、宽
    void start();
    
    void stop();
}
class Flash implements USB{
​
    @Override
    public void start() {
        System.out.println("U 盘开始工作");
    }
​
    @Override
    public void stop() {
        System.out.println("U 盘结束工作");
    }
}
class Printer implements USB{
    @Override
    public void start() {
        System.out.println("打印机开启工作");
    }
​
    @Override
    public void stop() {
        System.out.println("打印机结束工作");
    }
    
}
​

类的内部成员之五:内部类

当一个事物的内部,还有一个部分需要一个完整的结构进行描述,而这个内部的完整的结构又只为外部事物提供服务,那么整个内部的完整结构最好使用内部类。

/*
 * 类的内部成员之五:内部类
 * 
 * 1.Java中允许将一个类A声明在另一个类B中,则类A就是内部类,类B就是外部类.
 * 
 * 2.内部类的分类:成员内部类   VS  局部内部类(方法内、代码块内、构造器内)
 *      
 * 3.成员内部类
 *  》作为外部类的成员,
 *      - 调用外部类的结构
 *      - 可以被static修饰
 *      - 可以被4种不同的权限修饰
 * 
 *  》作为一个类,
 *      - 类内可以定义属性、方法、构造器等
 *      - 可以被final修饰,表示此类不能被继承。言外之意,不使用final,就可以被继承
 *      - 可以abstract修饰
 * 
 * 4.关注如下的3个问题
 *   》 如何实例化成员内部类的对象
 *   》 如何在成员内部类中区分调用外部类的结构
 *   》 开发中局部内部类的使用  见《InnerClassTest1.java》
 */
public class InnerClassTest {
    public static void main(String[] args) {
        
        //创建Dog实例(静态的成员内部类)
        Person.Dog dog = new Person.Dog();
        dog.show();
        
        //创建Bird实例(非静态的成员内部类)
//      Person.Bird bird = new Person.Bird();
        Person p = new Person();
        Person.Bird bird = p.new Bird();
        bird.sing();
        
        System.out.println();
        
        bird.display("喜鹊");
    }
}
class Person{
    String name = "李雷";
    int age;
    
    public void eat(){
        System.out.println("人,吃饭");
    }
    
    //静态成员内部类
    static class Dog{
        String name;
        int age;
        
        public void show(){
            System.out.println("卡拉是条狗");
//          eat();
        }
    }
    
    //非静态成员内部类
    class Bird{
        String name = "杜鹃";
        public Bird(){
            
        }
        
        public void sing(){
            System.out.println("我是一只猫头鹰");
            Person.this.eat();//调用外部类的非静态属性
            eat();
            System.out.println(age);
        }
        
        public void display(String name){
            System.out.println(name);   //方法的形参
            System.out.println(this.name);  //内部类的属性
            System.out.println(Person.this.name);   //外部类的属性
        }
    }
    public void method(){
        //局部内部类
        class AA{
            
        }
    }
    
    {
        //局部内部类
        class BB{
            
        }
    }
    
    public Person(){
        //局部内部类
        class CC{
            
        }
    }
}
​
public class InnerClassTest1 {
    
//  开发中很少见
    public void method(){
//      局部内部类
        class AA{
            
        }
    }
    
//  返回一个实现了Comparable接口的类的对象
    public Comparable getComparable(){
        
//      创建一个实现了Comparable接口的类:局部内部类
        //方式一:
//      class MyComparable implements Comparable{
//
//          @Override
//          public int compareTo(Object o) {
//              return 0;
//          }
//          
//      }
//      
//      return new MyComparable();
        
        //方式二:
        return new Comparable(){
​
​
            @Override
            public int compareTo(Object o) {
                return 0;
            }
            
        };
        
    }
}
​
/*
 * 1.匿名内部类不能定义任何静态成员、方法和类,只能创建匿名内部类的一个实例。
 * 一个匿名内部类一定是在new的后面,用其隐含实现一个接口或实现一个类。
 * 
 * 2.格式:
 *      new 父类构造器(实参列表)|实现接口(){
 *              //匿名内部类的类体部分
 *      }
 * 
 * 3.匿名内部类的特点
 *      > 匿名内部类必须继承父类或实现接口
 *      > 匿名内部类只能有一个对象
 *      > 匿名内部类对象只能使用多态形式引用
 */
interface Product{
    public double getPrice();
    public String getName();
}
public class AnonymousTest{
    public void test(Product p){
        System.out.println("购买了一个" + p.getName() + ",花掉了" + p.getPrice());
    }
    public static void main(String[] args) {
        AnonymousTest ta = new AnonymousTest();
        //调用test方法时,需要传入一个Product参数,
        //此处传入其匿名实现类的实例
        ta.test(new Product(){
            public double getPrice(){
                return 567.8;
            }
            public String getName(){
                return "AGP显卡";
            }
        });
    }
}
​
public class InnerClassTest {
    
//  public void onCreate(){
//  
//  int number = 10;
//  
//  View.OnClickListern listener = new View.OnClickListener(){
//      
//      public void onClick(){
//          System.out.println("hello!");
//          System.out.println(number);
//      }
//      
//  }
//  
//  button.setOnClickListener(listener);
//  
//}
​
    /*
     * 在局部内部类的方法中(比如:show)如果调用局部内部类所声明的方法(比如:method)中的局部变量(比如:num)的话,
     * 要求此局部变量声明为final的。
     * 
     * jdk 7及之前版本:要求此局部变量显式的声明为final的
     * jdk 8及之后的版本:可以省略final的声明
     * 
     */
    public void method(){
        //局部变量
        int num = 10;
        
        class AA{
            
            public void show(){
//              num = 20;   //Local variable num defined in an enclosing scope must be final or effectively final
                System.out.println(num);
            }
        }
    }
}
​
​

异常处理

异常概述与异常体系结构

ERRor:java虚拟机无法解决的严重问题,比如JVM内部错误资源耗尽等严重情况。比如:StackOverflowError和OOM。一般不编写针对性的代码进行处理。

/*
 * Java虚拟机无法解决的严重问题。如:JVM系统内部错误、资源耗尽等严重情况。
 * 比如:StackOverflowError和OOM。
 * 一般不编写针对性的代码进行处理。
 * 
 */
public class ErrorTest {
	public static void main(String[] args) {
		//1.栈溢出:java.lang.StackOverflowError
//		main(args);
		//2.堆溢出:java.lang.OutOfMemoryError
//		Integer[] arr = new Integer[1024*1024*1024];
		
	}
}
  • Exception:其它因编程错误或偶然的外在因素导致的一般性问题,可以使用针对性的代码进行处理。例如:

    • 空指针访问
    • 试图读取不存在的文件
    • 网络连接中断
    • 数组角标越界

对于这些错误,一般有两种解决方法:

一是遇到错误就终止程序的运行。
另一种方法是由程序员在编写程序时,就考虑到错误的检测、错误消息的提示,以及错误的处理。
捕获错误最理想的是在编译期间,但有的错误只有在运行时才会发生。比如:除数为0,数组下标越界等

异常分类:编译时异常和运行时异常

运行时异常
是指编译器不要求强制处置的异常。一般是指编程时的逻辑错误,是程序员应该积极避免其出现的异常。java.lang.RuntimeException类及它的子类都是运行时异常。
对于这类异常,可以不作处理,因为这类异常很普遍,若全处理可能会对程序的可读性和运行效率产生影响。
编译时异常
是指编译器要求必须处置的异常。即程序在运行时由于外界因素造成的一般性异常。编译器要求Java程序必须捕获或声明所有编译时异常。
对于这类异常,如果程序不处理,可能会带来意想不到的结果。

常见异常

import java.io.File;
import java.io.FileInputStream;
import java.util.Date;
import java.util.Scanner;
import org.junit.Test;

/*
 * 一、java异常体系结构
 * 
 * java.lang.Throwable
 * 		|----java.lang.Error:一般不编写针对性的代码进行处理
 * 		|----java.lang.Exception:可以进行异常处理
 * 			|----编译时异常(checked)
 * 				|----IOEXception
 * 					|----FileNotFoundException
 * 				|----ClassNotFoundException
 * 			|----运行时异常(unchecked)
 * 				|----NullPointerException
 * 				|----ArrayIndexOutOfBoundsException
 * 				|----ClassCaseException
 * 				|----NumberFormatException
 * 				|----InputMismatchException
 * 				|----ArithmaticException
 * 
 * 面试题:常见的异常有哪些?举例说明
 * 
 */
public class ExceptionTest {

	// ******************以下是编译时异常***************************
	@Test
	public void test7() {
//		File file = new File("hello.txt");
//		FileInputStream fis = new FileInputStream(file);
//		
//		int data = fis.read();
//		while(data != -1){
//			System.out.print((char)data);
//			data = fis.read();
//		}
//		
//		fis.close();
	}

	// ******************以下是运行时异常***************************
	// ArithmeticException
	@Test
	public void test6() {
		int a = 10;
		int b = 0;
		System.out.println(a / b);
	}

	// InputMismatchException
	@Test
	public void test5() {
		Scanner scanner = new Scanner(System.in);
		int score = scanner.nextInt();
		System.out.println(score);

		scanner.close();
	}

	// NumberFormatException
	@Test
	public void test4() {
		String str = "123";
		str = "abc";
		int num = Integer.parseInt(str);
	}

	// ClassCaseException
	@Test
	public void test3() {
		 Object obj = new Date();
		 String str = (String)obj;
	}

	// ArrayIndexOutOfBoundsException
	@Test
	public void test2() {
		// int[] arr = new int[10];
		// System.out.println(arr[10]);

		// String str = "abc";
		// System.out.println(str.charAt(3));
	}

	// NullPointerException
	@Test
	public void test1() {
		// int[] arr = null;
		// System.out.println(arr[3]);

		// String str = "abc";
		// str = null;
		// System.out.println(str.charAt(0));
	}
}

异常处理机制一:try-catch-finally

在编写程序时,经常要在可能出现错误的地方加上检测的代码,如进行x/y运算时,要检测分母为0,数据为空,输入的不是数据而是字符等。过多的if-else分支会导致程序的代码加长、臃肿,可读性差。因此采用异常处理机制。

Java异常处理:

Java采用的异常处理机制,是将异常处理的程序代码集中在一起,与正常的程序代码分开,使得程序简洁、优雅,并易于维护。

方式一:try-catch-finally

方式二:throws + 异常类型

Java异常处理的方式: try-catch-finally

try
捕获异常的第一步是用try{…}语句块选定捕获异常的范围,将可能出现异常的代码放在try语句块中。
catch(Exceptiontypee)
在catch语句块中是对异常对象进行处理的代码。每个try语句块可以伴随一个或多个catch语句,用于处理可能产生的不同类型的异常对象。
捕获异常的有关信息:与其它对象一样,可以访问一个异常对象的成员变量或调用它的方法。
getMessage() 获取异常信息,返回字符串
printStackTrace() 获取异常类名和异常信息,以及异常出现在程序中的位置。返回值void。

finally
捕获异常的最后一步是通过finally语句为异常处理提供一个统一的出口,使得在控制流转到程序的其它部分以前,能够对程序的状态作统一的管理。
不论在try代码块中是否发生了异常事件,catch语句是否执行,catch语句是否有异常,catch语句中是否有return,finally块中的语句都会被执行。
finally语句和catch语句是任选的

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

import org.junit.Test;

/*
 * 异常的处理:抓抛模型
 * 
 * 过程一:“抛”:程序在征程执行过程中,一旦出现异常,就会在异常代码处生成一个对应异常类的对象
 * 			 并将此对象抛出。
 * 			一旦抛出对象以后,其后的代码就不再执行。
 * 
 * 过程二:“抓”:可以理解为异常的处理方式:① try-catch-finally  ② throws
 * 
 * 二、try-catch-finally的使用
 * 
 * try{
 * 		//可能出现异常的代码
 * }catch(异常类型1 变量名1){
 * 		//处理异常的方式1
 * }catch(异常类型2 变量名2){
 * 		//处理异常的方式2
 * }catch(异常类型3 变量名3){
 * 		//处理异常的方式3
 * }
 * ...
 * finally{
 * 		//一定会执行的代码
 * }
 * 
 * 说明:
 * 1.finally是可选的。
 * 2.使用try将可能出现异常代码包装起来,在执行过程中,一旦出现异常,就会生成一个对应异常类的对象,根据此对象
 *   的类型,去catch中进行匹配。
 * 3.一旦try中的异常对象匹配到某一个catch时,就进入catch中进行异常的处理。一旦处理完成,就跳出当前的
 *   try-catch结构(在没有写finally的情况)。继续执行其后的代码。
 * 4.catch中的异常类型如果没有子父类关系,则谁声明在上,谁声明在下无所谓。
 *   catch中的异常类型如果满足子父类关系,则要求子类一定声明在父类的上面。否则,报错
 * 5.常用的异常对象处理的方式: ① String  getMessage()    ② printStackTrace()
 * 6.在try结构中声明的变量,再出了try结构以后,就不能再被调用,例65行:System.out.println(num);
 * 7.try-catch-finally结构可以嵌套  
 * 
 * 体会1:使用try-catch-finally处理编译时异常,是得程序在编译时就不再报错,但是运行时仍可能报错。
 *     相当于我们使用try-catch-finally将一个编译时可能出现的异常,延迟到运行时出现。
 *     
 * 体会2:开发中,由于运行时异常比较常见,所以我们通常就不针对运行时异常编写try-catch-finally了。
 *      针对于编译时异常,我们说一定要考虑异常的处理。
 */
public class ExceptionTest1 {
	
	@Test
	public void test2(){
		try{
			File file = new File("hello.txt");
			FileInputStream fis = new FileInputStream(file);
			
			int data = fis.read();
			while(data != -1){
				System.out.print((char)data);
				data = fis.read();
			}
			
			fis.close();
		}catch(FileNotFoundException e){
			e.printStackTrace();
		}catch(IOException e){
			e.printStackTrace();
		}
	}

	@Test
	public void test1(){
		
		String str = "123";
		str = "abc";
		try{
			int num = Integer.parseInt(str);	
			
			System.out.println("hello-----1");
		}catch(NumberFormatException e){
//			System.out.println("出现数值转换异常了,不要着急....");
			//String getMessage():
//			System.out.println(e.getMessage());
			//printStackTrace():
			e.printStackTrace();
		}catch(NullPointerException e){
			System.out.println("出现空指针异常了,不要着急....");
		}catch(Exception e){
			System.out.println("出现异常了,不要着急....");
		}
//		System.out.println(num);
		
		System.out.println("hello----2");
	}
}

 finally的使用

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

import org.junit.Test;

/*
 * try-catch-finally中finally的使用:
 * 
 * 1.finally是可选的。
 * 2.finally中声明的是一定会被执行的代码。即使catch中又出现异常了,try中有return语句,catch中有
 *   return语句等情况。
 * 3.像数据库连接、输入输出流、网络编程Socket等资源,JVM是不能自动的回收的,我们需要自己手动的进行资源的
 *   释放。此时的资源释放,就需要声明在finally中。
 * 
 */
public class FinallyTest {

	@Test
	public void test2() {
		FileInputStream fis = null;
		try {
			File file = new File("hello1.txt");//文件可能不存在,而出现异常
			fis = new FileInputStream(file);

			int data = fis.read();
			while (data != -1) {
				System.out.print((char) data);
				data = fis.read();
			}

		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			try {
				if (fis != null)
					fis.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}

	@Test
	public void testMethod() {
		int num = method();
		System.out.println(num);
	}

	public int method() {

		try {
			int[] arr = new int[10];
			System.out.println(arr[10]);
			return 1;
		} catch (ArrayIndexOutOfBoundsException e) {
			e.printStackTrace();
			return 2;
		} finally {
			System.out.println("我一定会被执行");
			return 3;
		}
	}

	@Test
	public void test1() {
		try {
			int a = 10;
			int b = 0;
			System.out.println(a / b);
		} catch (ArithmeticException e) {
			// e.printStackTrace();

			int[] arr = new int[10];
			System.out.println(arr[10]);

		} catch (Exception e) {
			e.printStackTrace();
		}
		// System.out.println("我好慢呀~~~");
		finally {
			System.out.println("我好慢呀~~~");
		}
	}
}

异常处理机制二:throws

声明抛出异常是Java中处理异常的第二种方式
如果一个方法(中的语句执行时)可能生成某种异常,但是并不能确定如何处理这种异常,则此方法应显示地声明抛出异常,表明该方法将不对这些异常进行处理,而由该方法的调用者负责处理。
在方法声明中用throws语句可以声明抛出异常的列表,throws后面的异常类型可以是方法中产生的异常类型,也可以是它的父类。
 

/*
 * 异常处理的方式二:throws + 异常类型
 * 
 * 1. "throws + 异常类型"写在方法的声明处。指明此方法执行时,可能会抛出的异常类型。
 *     一旦当方法体执行时,出现异常,仍会在异常代码处生成一个异常类的对象,此对象满足throws后异常
 *     类型时,就会被抛出。异常代码后续的代码,就不再执行!
 *
 *     关于异常对象的产生:① 系统自动生成的异常对象
 * 					② 手动生成一个异常对象,并抛出(throw)
 *     
 * 2. 体会:try-catch-finally:真正的将异常给处理掉了。
 *        throws的方式只是将异常抛给了方法的调用者。  并没有真正将异常处理掉。  
 * 
 */
public class ExceptionTest2 {
	
	public static void main(String[] args){
		try {
			method2();
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		method3();
	}
	
	public static void method3(){
		try {
			method2();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	public static void method2() throws IOException{
		method1();
	}

	
	public static void method1() throws FileNotFoundException,IOException{
		File file = new File("hello1.txt");
		FileInputStream fis = new FileInputStream(file);
		
		int data = fis.read();
		while(data != -1){
			System.out.print((char)data);
			data = fis.read();
		}
		
		fis.close();
		
		System.out.println("hahaha!");
	}
}

 

import java.io.FileNotFoundException;
import java.io.IOException;

/*
 * 方法重写的规则之一:
 * 子类重写的方法抛出的异常类型不大于父类被重写的方法抛出的异常类型
 * 
 */
public class OverrideTest {
    
    public static void main(String[] args) {
        OverrideTest test = new OverrideTest();
        test.display(new SubClass());
    }
    
    public void display(SuperClass s){
        try {
            s.method();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    
}
class SuperClass{
    
    public void method() throws IOException{
        
    }
}
class SubClass extends SuperClass{
    public void method()throws FileNotFoundException{
        
    }
}
/* 3. 开发中如何选择使用try-catch-finally 还是使用throws?
 *   3.1 如果父类中被重写的方法没有throws方式处理异常,则子类重写的方法也不能使用throws,意味着如果
 *       子类重写的方法中有异常,必须使用try-catch-finally方式处理。
 *   3.2 执行的方法a中,先后又调用了另外的几个方法,这几个方法是递进关系执行的。我们建议这几个方法使用throws
 *       的方式进行处理。而执行的方法a可以考虑使用try-catch-finally方式进行处理。
 */

手动抛出异常

Java异常类对象除在程序执行过程中出现异常时由系统自动生成并抛出,也可根据需要使用人工创建并抛出。

  • 首先要生成异常类对象,然后通过throw语句实现抛出操作(提交给Java运行环境)
  • 可以抛出的异常必须是Throwable或其子类的实例。下面的语句在编译时将会产生语法错误:
public class StudentTest {
	public static void main(String[] args) {
		try {
			Student s = new Student();
//		s.regist(1001);
			s.regist(-1001);
			System.out.println(s);
		} catch (Exception e) {
//			e.printStackTrace();
			System.out.println(e.getMessage());
		}
	}
}
class Student{
	private int id;
	
	public void regist(int id) throws Exception{
		if(id > 0){
			this.id = id;
		}else{
//			System.out.println("您输入的数据非法!");
			//手动抛出异常
//			throw new RuntimeException("您输入的数据非法!");
			throw new Exception("您输入的数据非法!");
			
		}
	}

	@Override
	public String toString() {
		return "Student [id=" + id + "]";
	}
	
}

用户自定义异常类

  • 一般地,用户自定义异常类都是RuntimeException的子类。
  • 自定义异常类通常需要编写几个重载的构造器。
  • 自定义异常需要提供serialVersionUID
  • 自定义的异常通过throw抛出。
  • 自定义异常最重要的是异常类的名字,当异常出现时,可以根据名字判断异常类型。
/*
 * 如何自定义异常类?
 * 1.继承于现有的异常结构:RuntimeException 、Exception
 * 2.提供全局常量:serialVersionUID
 * 3.提供重载的构造器
 * 
 */
public class MyException extends RuntimeException{
	static final long serialVersionUID = -7034897193246939L;
	
	public MyException(){
		
	}
	
	public MyException(String msg){
		super(msg);
	}
}

 练习1——ReturnExceptionDemo类

public class ReturnExceptionDemo {
	static void methodA() {
		try {
			System.out.println("进入方法A");
			throw new RuntimeException("制造异常");
		} finally {
			System.out.println("用A方法的finally");
		}
	}

	static void methodB() {
		try {
			System.out.println("进入方法B");
			return;
		} finally {
			System.out.println("调用B方法的finally");
		}
	}

	public static void main(String[] args) {
		try {
			methodA();
		} catch (Exception e) {
			System.out.println(e.getMessage());
		}
			
		methodB();
	}
}

练习2

/*
 * 编写应用程序EcmDef.java,接收命令行的两个参数,
 * 		要求不能输入负数,计算两数相除。
 * 		对 数 据 类 型 不 一 致(NumberFormatException)、
 * 		缺 少 命 令 行 参 数(ArrayIndexOutOfBoundsException、
 * 		除0(ArithmeticException)及输入负数(EcDef自定义的异常)进行异常处理。
 *
 * 提示:
 * 		(1)在主类(EcmDef)中定义异常方法(ecm)完成两数相除功能。
 * 		(2)在main()方法中使用异常处理语句进行异常处理。
 * 		(3)在程序中,自定义对应输入负数的异常类(EcDef)。
 * 		(4)运行时接受参数java EcmDef2010//args[0]=“20”args[1]=“10”
 * 		(5)Interger类的static方法parseInt(Strings)将s转换成对应的int值。
 * 		如:int a=Interger.parseInt(“314”);//a=314;
 */
public class EcmDef {
	public static void main(String[] args) {
		try {
			int i = Integer.parseInt(args[0]);
			int j = Integer.parseInt(args[0]);
			
			int result = ecm(i,j);
			
			System.out.println(result);
		} catch (NumberFormatException e) {
			System.out.println("数据类型不一致");
		}catch (ArrayIndexOutOfBoundsException e){
			System.out.println("缺少命令行参数");
		}catch (ArithmeticException e){
			System.out.println("除0");
		}catch (EcDef e) {
			System.out.println(e.getMessage());
		}
	}
	
	public static int ecm(int i, int j) throws EcDef{
		if(i < 0 || j < 0){
			throw new EcDef("分子或分母为负数了!");
		}
		return i / j;
	}
}

练习2的自定义异常类——EcDef

//自定义异常类
public class EcDef extends Exception {

	static final long serialVersionUID = -33875164229948L;

	public EcDef() {
		
	}

	public EcDef(String msg) {
		super(msg);
	}
}

异常总结

多线程

常用类

枚举与注解

集合

泛型

IO流

网络编程

反射与动态代理

java8新特性

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值