3.数组、面向对象和异常(狂神说)

Java数组

数组的定义

  • 数组是相同类型数据的有序集合
  • 数组描述的是相同类型的若干数据,按照一定先后次序排序组合而成
  • 其中,每一个数据称作一个数组元素,每个数组元素可以通过下标访问它们

数组的声明创建

  • 首先必须声明数组变量,才能在程序中使用数组。
dataType[] arrayRefVar; //首选
dataType arrayRefVar[]; //效果相同,但不是首选
  • Java语言使用new操作符来创建数组,语法如下
dataType[] arrayRefVar = new dataType[arraySize]; //int[] nums=new int[10]
  • 数组的元素是通过索引访问的,数组索引从0开始
  • 获取数组长度:arrays.length
int[] nums; //1.声明一个数组
nums = new int[3]; //2.创建一个数组
//3.给数组元素赋值
nums[0]=1;
nums[1]=2;
nums[2]=3;
for (int num : nums) { //打印数组所有元素
    System.out.println(num);
}

内存分析

image

image

数组的三种初始化

  • 静态初始化
//静态初始化:创建+赋值
int[] a={1,2,3};
Man[] mans={new Man(1,1),new Man(2,2)}
  • 动态初始化
//包含默认初始化
int[] a=new int[2]; //默认值为0
a[0]=1;
a[1]=2;

默认初始化

  • 数组是引用类型,它的元素相当于类的实例变量,因此数组一经分配空间,其中的每个元素也被按照实例变量同样的方式被隐式初始化。

数组的基本特点

  1. 其长度是确定的,数组一旦被创建,它的大小就是不可改变的。

  2. 其元素必须是相同类型,不允许出现混合类型。

  3. 数组中的元素可以是任何数据类型,包括基本类型和引用类型。

  4. 数组变量属于引用类型,数组也可以看作对象,其中每个元素相当于该对象的成员变量。

    数组本身就是对象,Java中对象是在堆中的,因此数组无论保存原始类型还是其他对象类型,

    数组本身是在堆中的。

数组的使用

  • For-Each循环
int[] arrays = {1,2,3,4,5};
//打印全部的数组元素 JDK1.5 没有下标
for (int array : arrays) {
    System.out.println(array);
}
  • 数组作方法入参
//打印数组元素
public static void printArray(int[] a){
    for (int i = 0; i < a.length; i++) {
        System.out.print(a[i]+" ");
    }
}
  • 数组作返回值
//反转数组
public static int[] reverse(int[] arrays){
    int[] result = new int[arrays.length];
    //反转的操作
    for (int i = 0; i < arrays.length; i++) {
        result[i] = arrays[arrays.length-i-1];
    }
    return result;
}

多维数组

  • 多维数组可以看成数组的数组,比如二维数组就是一个特殊的数组,其每一个元素都是一个一维数组。
int arr[][] = new int[3][2]; //二维数组,三行两列

Arrays类

  • 数组的工具类java.util.Arrays

  • 由于数组对象本身并没有什么方法可以供我们使用,但API提供了一个工具类Arrays供我们使用。

  • Array类中的方法都是static修饰的静态方法,使用时直接使用类名进行调用,可以不用对象调用。

  • 常用功能

    • 给数组赋值:fill方法。
    • 排序:sort方法,升序。
    • 比较数组:equals方法比较数组中元素值是否相等。
    • 查找数组元素:binarySearch对排序好的数组进行二分查找法操作。
    int[] a = {1,2,3,4,9000,32145,451,21};
    System.out.println(a); // [I@28d93b30 (hashcode)
    
    //Arrays.toString 打印数组元素
    System.out.println(Arrays.toString(a)); //[1, 2, 3, 4, 9000, 32145, 451, 21]
    
    //二分法查找某值 返回下标
    System.out.println(Arrays.binarySearch(a, 9000)); // 4
    
    //填充
    Arrays.fill(a,2,4,0); //数组[a[2]~a[4])之间填充0
    System.out.println(Arrays.toString(a)); //[1, 2, 0, 0, 9000, 32145, 451, 21]
    
    //升序排序
    Arrays.sort(a);
    
    

稀疏数组

image

image

//创建一个二维数组 11*11  0:没有棋子,1:黑棋  2:白棋
int[][] array1 = new int[11][11];
array1[1][2] = 1;
array1[2][3] = 2;
//输出原始的数组
System.out.println("原始的数组:");
for (int[] array : array1) {
    for (int i : array) {
        System.out.print(i+"\t");
    }
    System.out.println();
}

//转换为稀疏数组保存
//1.有效值的个数
int sum = 0; //有效值总数
for (int i = 0; i < 11; i++) {
    for (int j = 0; j < 11; j++) {
        if(array1[i][j]!=0){
            sum++;
        }
    }
}
//2.创建一个稀疏数组
int[][] array2 = new int[sum+1][3];
array2[0][0] = 11;
array2[0][1] = 11;
array2[0][2] = sum;

//3.遍历二维数组,将有效值存放到稀疏数组
int count = 0;
for (int i = 0; i < array1.length; i++) {
    for (int j = 0; j < array1[i].length; j++) {
        if(array1[i][j]!=0){
            count++;
            array2[count][0] = i;
            array2[count][1] = j;
            array2[count][2] = array1[i][j];
        }
    }
}

//4.输出稀疏数组
System.out.println("稀疏数组:");
for (int i = 0; i < array2.length; i++) {
    for (int j = 0; j < array2[i].length; j++) {
        System.out.print(array2[i][j]+"\t");
    }
    System.out.println();
}
/* 结果:
输出原始的数组
0	0	0	0	0	0	0	0	0	0	0	
0	0	1	0	0	0	0	0	0	0	0	
0	0	0	2	0	0	0	0	0	0	0	
0	0	0	0	0	0	0	0	0	0	0	
0	0	0	0	0	0	0	0	0	0	0	
0	0	0	0	0	0	0	0	0	0	0	
0	0	0	0	0	0	0	0	0	0	0	
0	0	0	0	0	0	0	0	0	0	0	
0	0	0	0	0	0	0	0	0	0	0	
0	0	0	0	0	0	0	0	0	0	0	
0	0	0	0	0	0	0	0	0	0	0	
稀疏数组
11	11	2	
1	2	1	
2	3	2	
*/

面向对象

  • 面向对象编程(Object-Oriented Programming, OOP)
  • 本质:以类的方式组织代码,以对象的组织(封装)数据。
  • 抽象
  • 三大特性
    • 封装
    • 继承
    • 多态
  • 从认识论的角度考虑是先有对象后有类。对象是具体的事物,类是对象的抽象。
  • 从代码运行角度考虑是先有类后有对象。类是对象的模板

创建与初始化对象

  • 使用new来创建对象。
  • 使用new关键字创建的时候,除了分配内存之外,还会给创建好的对象进行默认的初始化,以及对类中构造器的调用。
  • 类中的构造器也被称为构造方法,创建对象时必须要调用。有以下特点:
    • 必须和类的名字相同
    • 没有返回类型,也不能写void
  • 一个类即使什么都不写,也会存在一个默认的构造方法

构造器

public class Person {
    //一个类即使什么都不写,也会存在一个默认的无参构造方法
    //显示地定义构造器
    String name;
    
    //作用:1. 使用new关键字,本质是在调用构造器
    //2. 用来初始化对象的值
    public Person(){} //无参构造
    
    //有参构造 3.一旦定义了有参构造,无参就必须显示定义
    public Person(String name){
        this.name=name;
    }
	//Alt+insert 快捷键插入构造方法
}

内存分析

//定义一个宠物类
public class Pet {
    public String name; //默认 null
    public int age; 	//默认 0
    //无参构造

    public void shout(){
        System.out.println("叫了一声");
    }
}
//应用类,创建调用对象
public class Application {
    public static void main(String[] args) {
        
        Pet dog = new Pet();

        dog.name = "旺财";
        dog.age = 3;
        dog.shout();
    }
}
  • 对象通过引用类型来操作:栈 - - ->堆

image

封装

  • 该露的露,该藏的藏
    • 我们程序设计要追求“高内聚,低耦合”。高内聚就是类的内部数据细节由自己完成,不允许外部干涉;低耦合:仅暴露少量的方法给外部使用
  • 封装(数据的隐藏)
    • 通常,应禁止直接访问一个对象中数据的实际表示,而应通过操作接口来访问,称为信息隐藏。
  • 作用
  1. 提高程序的安全性,保护数据
  2. 隐藏代码的实现细节
  3. 统一接口
  4. 系统可维护性增加了

继承

  • 继承的本质是对某一批类的抽象,从而实现对世界更好地建模。
  • extends的意思是”扩展“。子类是父类的扩展,使用关键字extends来表示。
  • Java中类只有单继承,没有多继承!一个类只能继承一个父类。
  • 继承是类与类之间的一种关系,此外还有依赖、组合、聚合等。
  • 继承关系的两个类,一个为子类(派生类),一个为父类(基类)子类继承父类。
  • 子类和父类之间,从意义上讲应该具有”is a“的关系。
//学生类(子类)继承 人类(父类)
public class Student extends Person{ /*Person extends Object*/
    ...
}
  • 子类继承了父类,就会拥有父类的全部方法,而private私有属性及方法无法继承
  • 在Java中,所有类,都默认直接或间接继承Object类 (Ctrl+H 可以查看类关系)
  • 被final修饰的类,无法被继承(断子绝孙)。

super & this

  1. super()调用父类的构造方法,必须在构造方法的第一个
  2. super必须只能出现在子类的方法或构造方法中
  3. super()this()不能同时调用构造方法,因为this也必须写在第一行
  • super与this的区别:super代表父类对象的引用,只能在继承条件下使用;this调用自身对象,没有继承也可以使用。
super(); //隐藏代码,默认调用了父类的无参构造,要写只能写第一行

方法的重写

  • 重写:子类的方法必须与父类方法必须一致,方法体不同。
  • 重写是方法的重写,与属性无关
  • 重写方法只与非静态方法有关,与静态方法无关(静态方法不能被重写)
  • 静态方法属于类,非静态方法属于对象
  • 注意点:
  1. 方法名、参数列表必须相同
  2. 修饰符范围可以扩大,不能缩小(public>protect>private)
  3. 抛出的异常 范围可以被缩小,不能扩大
  4. static(属于类,不属于实例),final(常量方法),private(私有)修饰的方法不能重写

多态

  • 动态编译:类型

  • 即同一方法可以根据发送对象的不同而采用不同的行为方式

  • 一个对象的实际类型是确定的,但可以指向对象的引用可以有很多

  • 多态存在条件

    • 有继承关系
    • 子类重写父类方法
    • 父类引用指向子类对象

    image

注意点:

  1. 多态是方法的多态,没有属性的多态
  2. 父类和子类,有联系 类型转换异常: ClassCastException
  3. 存在条件:继承关系,方法需要重写,父类引用指向子类对象!

instanceof和类型转换

  • instanceof 引用类型比较,判断一个对象是什么类型
public static void main(String[] args) {

    // Object > String
    // Objest > Person > Student
    // Objest > Person > Teacher
    Object object = new Student();
	// X instanceof Y,X引用指向的对象是不是Y的子类
    System.out.println(object instanceof Student); //true
    System.out.println(object instanceof Person); //true
    System.out.println(object instanceof Teacher); //false
    System.out.println(object instanceof Object); //true
    System.out.println(object instanceof String); //false
	
    //类型之间的转化:父-子(高-低),低可以转换为高
    Person obj = new Syudent(); //只能用Person方法(重写了用子类重写过的方法)
    (Student)obj.go(); //强转之后可以用Student方法(Student->go())
}

类型转换

  1. 父类引用指向子类的对象
  2. 把子类转换为父类,向上转型,会丢失自己原来的一些方法
  3. 把父类转换为子类,向下转型,强制转换,才调用子类方法
  4. 方便方法的调用(转型),减少重复的代码,简洁。

Static

  • 静态变量可以直接用类名访问,也称类变量。
  • 静态变量(或方法)对于类,所有对象(实例)所共享。
  • 静态区代码 加载类时一起被初始化,最早执行且只执行一次(第一次new)。
  • Math->随机数:
//静态导入包
import static java.lang.Math.random;

public class Application {
    public static void main(String[] args) {

        //第一种随机数,不用导包
        System.out.println(Math.random()); //0.7562202902634543

        //第二种随机数,静态导入包
        System.out.println(random()); //0.5391606223844663
    }
}

在创建对象是,执行顺序:静态代码块(最早,但只执行一次)、匿名代码块、构造方法;

抽象类(abstract)

  • abstract修饰的类就是抽象类,修饰的方法就是抽象方法。
  • 抽象类中可以没有抽象方法,但有抽象方法的类一定要声明为抽象类。
  • 抽象类不能使用new来创建对象,它是用来让子类继承的。
  • 抽象方法只有方法的声明,没有实现,让其子类实现。
  • 子类继承抽象类,必须实现抽象类的所有方法,否则该子类也要声明为抽象类。
//abstract 抽象类 类只能单继承(接口可以多继承)
public abstract class Action {

    //约束~有人帮我们实现~
    //抽象方法只有方法名,没有方法的实现
    public abstract void doSth();

    //1.不能new抽象类,只能靠子类去实现它,仅作为一个约束
    //2.抽象方法只能出现在抽象类中,抽象类可以有普通方法
    //3.抽象类有构造器,可以派生子类
    //4.抽象类的意义:约束,提高开发效率。但是类只能单继承,所以有局限 用的不多
}

接口(interface)

  • 普通类:只有具体实现

  • 抽象类:具体实现和规范(抽象方法)都有

  • 接口:只有规范,没有方法实现,专业的约束!约束与实现分离:面向接口编程~

  • 接口就是规范,定义的是一组规则,"你是什么…必须做什么…"的思想。

  • 接口的本质是约束,就像人间法律一样,制定好大家都遵守。

//interface接口,接口都要有继承类
//实现类(implements 可以继承多个接口)
//多继承,利用接口实现多继承
public interface UserService {
    //定义的属性都是常量,默认修饰 public static final
    public static final int AGE = 99; //一般不用
    //所有的定义的方法都是抽象的 默认public abstract
    public abstract void run();
    void add();
    void query();
    void delete();
}

注意点

  • 接口没有构造方法,不能被实例化
  • 实现类必须要重写接口中的方法
  • 实现类(implements) 可以实现多个接口

内部类

  • 内部类就是在一个类的内部再定义一个类,比如A类中定义了一个B类,那么B就是A的内部类,而A相对B来说就是外部类
    1. 成员内部类:可以操作外部类的私有属性及方法
    2. 静态内部类:static修饰,不能访问外部类私有属性
    3. 局部内部类:外部类的方法里定义的类
    4. 匿名内部类:没有名字初始化类

异常

  • 软件程序在运行过程中,经常可能遇到异常问题,异常英文(Exception),意思是例外,这些例外情况需要我们写程序做出合理的处理,而不至于让程序崩溃。
  • 异常指程序运行中出现的不期而至的各种状况:文件找不到,网络连接错误,非法参数等。
  • 异常发生在程序运行期间,它影响了正常的执行流程。

简单分类

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

image

异常处理机制

  • 抛出异常
  • 捕获异常
  • 异常处理关键字:try、catch、finally、throw、throws
public static void main(String[] args) {
    int a = 1;
    int b = 0;

    try { //try监控区域
        System.out.println(a/b);
    }catch (ArithmeticException e){ //catch 捕获异常
        System.out.println("程序出现异常,变量b不能为0");
    }catch (Exception e){
        e.printStackTrace();
    }finally { //一定会执行,处理善后工作,如关闭资源
        System.out.println("finally");
    }
    
    if(b==0){ //抛出异常一般在方法中使用
        throw new ArithmeticException(); //主动抛出异常
    }
}
//Ctrl+Alt+T 快捷键插入 try-catch

自定义异常

image

image

posted @ 2021-08-03 10:38  木兮君  阅读(66)  评论(0)    收藏  举报