狂神说 JavaSE入门笔记(三)

7 数组

7.1 数组概述

数组是相同类型数据的有序集合

可以通过下标来访问,下标从0开始

7.2 数组创建

声明创建

首先必须声明数组变量,才能在程序中使用数组。下面是声明数组变量的语法:

dataType[] arrayRefVar; // 首选的方法

dataType arrayRefVar[]; // 效果相同,但不是首选方法

Java语言使用new操作符来创建数组,语法如下:

arrayRefVar = new dataType[arraySize];

上面的语法语句做了两件事:

  1. 使用 dataType[arraySize] 创建了一个数组。
  2. 把新创建的数组的引用赋值给变量 arrayRefVar。

当然也可以使用一行代码完成声明和创建:

int[] numbers = new int[100];

可以通过numbers.length获取长度

初始化

有三种初始化

  • 静态初始化:

    int[] numbers = {1,2,3};
    Person[] people = {new Person(),new Person()};
    
  • 动态初始化:包含默认初始化

    int[] numbers = new int[10];
    b[0] = 1;
    
  • 默认初始化:数组是引用类型,其中的元素相当于类的实例变量,因此数组一经分配空间,其中的元素就按照实例变量同样的方式被隐式初始化。

内存分析

Java内存分为:

  • 堆:存放new的对象和数组
  • 栈:存放基本变量类型,包括具体数值。存放引用对象的变量,包括具体地址。
  • 方法区:包含所有class和static变量,方法区是特殊的堆。

声明数组时,数组并不存在,只是在栈中压入一个变量

创建数组时,数组才会存在,在堆中真正地开辟出空间

下标越界

访问下标超出数组大小会产生下标越界异常。

基本特点

  • 长度确定
  • 相同类型
  • 元素可以是任意的类型
  • 数组是引用类型,数组可以看成是对象,Java中的对象保存在堆中,无论数组对象中存什么数据类型,数组所占空间是在堆中的,返回的引用是在栈里的。引用:栈--->堆

7.3 数组使用

  • 使用for循环
  • 使用for-each循环即增强型for
  • 使用数组作为方法的参数
  • 使用数组作为返回值

7.4 多维数组

数组的元素还是数组

int[][] nums = new int[2][5];
int[][] numbers = {{1,2},{3,4},{5,6}};

7.5 Arrays类

数组的工具类java.util.Arrays

Arrays类中的方法都是static修饰的静态方法,可以直接使用类名调用

有以下常用功能

  • 给数组赋值:通过 fill 方法。
  • 对数组排序:通过 sort 方法,按升序。
  • 比较数组:通过 equals 方法比较数组中元素值是否相等。
  • 查找数组元素:通过 binarySearch 方法能对排序好的数组进行二分查找法操作。

7.6 冒泡排序

public static void sort(int[] array){
	for(int i=0;i<array.length-1;i++){
        
        boolean flag = false;//优化,跳过没有意义的比较轮次
        
        //减i是因为进行i次比较后,产生了i个最大或最小的数不需要进行下次比较
        for(int j=0;j<array.length-1-i;j++){
            if(array[j+1]<array[j])
                swap(array[j+1],array[j]);
        }
        if(flag == flase)
            break;
    }
}

7.7 稀疏数组

数组中大部分为0,或其他相同元素时,采用的一种压缩存储方式。

即只记录数组行列等基本信息,以及有效信息

8 面向对象

8.1 初识面向对象

面向过程

面向对象

以类的方式组织代码,以对象的方式封装数据

8.2 方法

回顾:如何定义方法

静态方法:可以通过类名调用

非静态方法:需要实例化对象,借用对象调用

形参实参

值传递引用传递

this关键字:用来表示当前对象本身,或当前类的一个实例,通过 this 可以调用本对象的所有方法和属性。

8.3 对象创建分析

使用new创建对象,创建时会

  • 分配内存空间
  • 默认初始化
  • 调用构造方法

构造方法

  • 方法名和类名相同
  • 没有返回类型,不能写成void

一旦定义了有参构造,无参构造就必须显式定义

new本质是在调用构造器

内存分析

内存分析

8.4 三大特性

封装

高内聚,低耦合

  • 模块内部联系紧密
  • 模块之间联系松散

封装:数据的隐藏,将数据封装起来,只暴露给外界接口

属性:private

方法:getter/setter

封装的优点:

  • 良好的封装能够减少耦合。
  • 类内部的结构可以自由修改。
  • 可以对成员变量进行更精确的控制。
  • 隐藏信息,实现细节。

继承

继承是类于类之间的关系,除此之外类与类之间的关系还有依赖,组合,聚合。继承:is a关系

Java只有单继承

控制访问与继承的规则

  • 父类中声明为 public 的方法在子类中也必须为 public。
  • 父类中声明为 protected 的方法在子类中要么声明为 protected,要么声明为 public,不能声明为 private。
  • 父类中声明为 private 的方法,不能够被继承。

Object类

在Java中,所有的类都继承了Object类

super关键字:表示当前类的父类

  • super调用父类的构造方法,必须在父类方法的第一个
  • super只能出现在子类方法或者是构造方法中
  • super和this不能同时调用构造方法

一般在写了带参数构造器后,都需要显式写出无参构造器

方法重写

重写是方法的重写与属性无关

静态方法:方法调用只和定义时的类有关,其实是通过类名在调用

非静态方法:重写,用实例化对象调用

父类

public class A {
    public static void test_static(){
        System.out.println("A=>test_static");
    }

    public void test(){
        System.out.println("A=>test");
    }
}

子类

public class B extends A{
    public static void test_static(){
        System.out.println("B=>test_static");
    }

    @Override
    public void test(){
        System.out.println("B=>test");
    }
}

测试

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

        B b = new B();
        A a= new B();

        b.test_static();
        a.test_static();

        a.test();
        b.test();
    }
}

输出

B=>test_static
A=>test_static
B=>test
B=>test

总结

  • 重写需要有继承关系,子类重写父类的关系
  • 方法名相同
  • 参数列表相同
  • 修饰符:范围可以扩大但不能缩小
    • 父类是protected子类改为public
  • 异常:可以缩小但不能扩大
    • 父类是Exception子类是ClassNotFoundException

不能重写的方法

  • static方法属于类,不属于实例
  • final修饰的方法
  • private 私有的方法无法重写

不能继承的类

  • 被final修饰的类

多态

动态编译:类型在执行时才能确定

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

一个对象的实际类型是确定的

但可以指向的引用类型就不一定了,父类引用可以指向子类的对象

父类

public class Person {
    public void run(){
        System.out.println("run");
    }
}

子类

public class Student extends Person{
    public void eat(){
        System.out.println("eat");
    }

    @Override
    public void run() {
        System.out.println("sonRun");;
    }
}

测试

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

        Student s1 = new Student();
        Person s2 = new Student();

        s1.run();//两个类型都有run方法且子类重写,执行子类的
        s2.run();

        s1.eat();//eat方法是子类独有的,父类无法调用
        //s2.eat();//报错
    }
}

输出

sonRun
sonRun
eat

总结:

多态是方法的多态,属性没有多态

多态存在条件:

  1. 有继承关系
  2. 子类重写父类的方法
  3. 父类引用指向子类对象

多态的实现可以有以下几种:

  1. 重写
  2. 接口
  3. 抽象类和抽象方法

8.5 instanceof

判断一个类型是不是某种类型

A instanceof B

看A和B有没有继承关系,

同级别类是无法通过编译的

8.6 类型转换

引用类型转换

同样的高转低要强制转换

低转高是直接实现可以的

如果子类转为父类就可能会丢失一些子类自己本来的方法

子类转父类 向上转型

父类转子类 向下转型 强制转换

8.7 static

static变量

static变量也称作静态变量,静态变量和非静态变量的区别是:静态变量被所有的对象所共享,在内存中只有一个副本【存放在方法区】,它当且仅当在类初次加载时会被初始化【加final和不加final的static变量初始化的位置不一样】。而非静态变量是对象所拥有的,在创建对象的时候被初始化,存在多个副本,各个对象拥有的副本互不影响。

static方法

static方法一般称作静态方法,由于静态方法不依赖于任何对象就可以进行访问,因此对于静态方法来说,是没有this的,因为它不依附于任何对象,既然都没有对象,就谈不上this了。并且由于这个特性,在静态方法中不能访问类的非静态成员变量和非静态成员方法,因为非静态成员方法/变量都是必须依赖具体的对象才能够被调用。

static静态代码块

static关键字还有一个比较关键的作用就是 用来形成静态代码块以优化程序性能。static块可以置于类中的任何地方,类中可以有多个static块。在类初次被加载的时候,会按照static块的顺序来执行每个static块,并且只会执行一次【根据class加载原理 每个类加载一次 使用双亲委托加载】。

初始化的顺序 静态代码块 > 构造代码块 > 构造函数

测试

public class Test {
    {
        System.out.println("匿名代码块");
    }

    static{
        System.out.println("静态代码块");
    }

    public Test(){
        System.out.println("构造方法");
    }

    public static void main(String[] args) {
        Test test = new Test();
        System.out.println("=========");
        Test test2 = new Test();
    }
}

执行

静态代码块
匿名代码块
构造方法
=========
匿名代码块
构造方法

static块可以用来优化程序性能,是因为它的特性:只会在类加载的时候执行一次,之后会按匿名代码块,构造方法的顺序执行

很多时候会将一些只需要进行一次的初始化操作都放在static代码块中进行。

静态内部类

​ pass

静态导包

import static java.lang.Math.random;//下面使用时可以直接调用random()

8.8 抽象类

abstract修饰符可以修饰方法也可以修饰类,abstract修饰方法就是抽象方法,abstract修饰类就是抽象类。

抽象类中可以没有抽象方法,但是一旦定义的抽象方法就必须声明为抽象类。抽象类也可以写普通方法。

抽象类:不能实例化,不能new,它就是用来让别人继承的

抽象方法:只有方法的声明,没有实现,子类需要实现。

子类继承抽象类就需要实现抽象方法,否则它也应当声明为抽象类


但是抽象类本质还是一个类,别人需要用extends继承,但是Java只有单继承

这是需要引入接口,接口可以实现多继承

思考:抽象类也存在构造器

8.9 接口

接口:只有规范。可以实现约束和实现分离--->面向接口编程

接口就是规范,定义的是一组规则。体现的是现实世界中“如果你是...则必须能...”的思想

接口的本质是契约,像法律,制定好了大家都要遵守

面向对象的精髓,是对对象的抽象,最能体现这一点的就是接口。

设计模式其实就是在研究:如何合理地去抽象


接口中定义方法都是抽象的,而且默认是public abstract,可以不写

接口中定义的属性都是常量,默认public static final,一般没人在接口里定义属性

使用接口时要注意锻炼自己的抽象思维能力

总结

  1. 接口是约束
  2. 接口定义一些方法,让不同的人实现
  3. 接口方法默认是public abstract
  4. 接口属性默认是public static final
  5. 接口不能被实例化,接口也没有构造方法
  6. implements可以实现多个接口

8.10 内部类

内部类就是在类的内部再定义一个类

一个java类可以有多个class,但只能有一个public class

  • 成员内部类

  • 静态内部类

  • 局部内部类

  • 匿名内部类

9 异常

什么是异常

程序运行时出现的不期而至的情况,文件找不到,网络链接失败等

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

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

异常体系结构

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

Exception 类是 Throwable 类的子类。除了Exception类外,Throwable还有一个子类Error 。

Java 程序通常不捕获错误。错误一般发生在严重故障时,它们在Java程序处理的范畴之外。

Error 用来指示运行时环境发生的错误。

例如,JVM 内存溢出。一般地,程序不会从错误中恢复。

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

graph TD A(Throwable) -->B(Exception) A -->C(Error) B -->D(IOException) B -->E(RuntimeException)

Error

Error类对象由Java虚拟机生成并抛出,大多数错误与代码编写者执行的操作无关。

Error是灾难性的致命的错误,是程序无法控制和处理的

Exception

Exception通常是可以被程序处理的。我们应当尽量避免一些异常的产生。

捕获和抛出异常

抛出异常

捕获异常

五个关键字

try,catch,finally,throw,throws

主动抛出异常

一般在方法中使用。

if(b == 0){
	throw new ArithmeticException();
}

在方法上抛出异常throws

public void method(int a,int b) throws IOException{
    }

自定义异常

在 Java 中你可以自定义异常。编写自己的异常类时需要记住下面的几点。

  • 所有异常都必须是 Throwable 的子类。
  • 如果希望写一个检查性异常类,则需要继承 Exception 类。
  • 如果你想写一个运行时异常类,那么需要继承 RuntimeException 类。

可以像下面这样定义自己的异常类:

class MyException extends Exception{
    }

实例

public class MyException extends Exception
{
  private int number;
  public MyException(int number)
  {
    this.number = number;
  } 
  @Override
  public String toString(){
  	return "MyException: "+number;
  }
}
原文地址:https://www.cnblogs.com/programthinking/p/13827573.html