Java基础

Java基础的笔记

Hello World

public class HelloWorld{
	public static void main(String[] args){
		System.out.println("hello world");
	}
}

运行机制

Java语言运行机制和运行过程

tips

  • java的代码.java可以转换为字节码文件**.class**,在不同的JVM上来运行,适用于各个系统。
  • java代码写到.java文件中,通过javac.exe对该java文件编译为.class文件,再通过java.exe运行.class文件,编辑器一键实现
  • Java 中的变量需要先声明后使用,所谓声明就是给他赋类型,一定要赋予类型!!!(好坑啊…)
  • 在一个java源文件可以声明多个class,但是只能最多有一个类声明为public,只能加到和文件名相同的类上
  • 用final定义常量名,一般使用大写字符,char类型不能用来定义常量,得用String
  • 每行命令后面要加分号(这么看python语法好方便啊…)
  • System.out.println(“a:”+a);通过加号来连接
  • 编译以后,会生成一个或多个字节码文件,有几个class就有几个字节码文件
  • 2进制用0b或0B开头,8进制用0开头,16进制用0x开头
  • 应用程序 = 算法 + 数据结构

注释

Java 中注释有三种类型:单行注释、多行注释、文档注释

单行注释
//内容

多行注释,不可以多行使用
/*
内容
*/

文档注释
注释内容可以被JDK提供的工具javadoc所解析,生成一套以网页文件形式体现该程序的说明文档
/**
@author 指定java程序的作者
@version 指定源文件的版本
*/


数据类型

数据类型
类型占用储存空间备注
byte1字节-128~127
short2字节
int4字节默认使用
long8字节long型常量后面要加‘l’或‘L’
float4字节精确到7位有效数字,声明时要加‘f’或‘F’
double8字节默认使用
char2字节1字符=2字节,使用一对’ ',可用做转义字符
boolean只能取true或false
  1. 自动类型提升
    • 当容量小的数据类型的变量和容量大的数据类型的变量做运算时,结果自动提升位容量大的数据类型
    • byte、char、short --> int --> long --> float --> double,当byte、char、short三种变量做运算时,结果为int型
  2. 强制类型转换
    • 需要使用强转符:()
    • 强制类型转换,可能跟会导致精度损失(将小数位截断,不是四舍五入)
  3. 声明String类型变量时,使用一对双引号"",可以和8种基本类型变量做运算,但只能是连接运算(+),看运算顺序,String在前面全部转换为字符串,不能简单的在强制(int)回去

运算符

算数运算符

/是除法;%是取余,结果的符号与被模数的符号相同;++/–在左边先运算后操作,在右边先操作后运算 ,不会改变变量的数据类型,+=,-=等等的比较运算符也不会改变数据类型

逻辑运算符

&逻辑与;&&短路与;|逻辑或; ||短路或; !非; ^异或

逻辑与/或要两者都执行,短路与/或当第一个能判断出来时,第二个便不再执行

条件运算符

条件运算符( ? : )也称为 “三元运算符”,可以嵌套使用

语法形式:布尔表达式 ? 表达式1 :表达式2

如果布尔表达式为true,返回1的值,否则返回2的值


输入

String 变量 = new.Scanner.(System.in).next();   // 输入字符串
int 变量 = new.Scanner.(System.in).nextInt();   // 输入数字

带标签的break和continue

// 在循环关键词前面加上标签名,便可以直接break或者continue此循环,否则默认最近的循环
public class Break {
    public static void main(String[] args) {
        lable:for (int i = 1; i <= 10; i++) {
            for (int j = 1; j <= 10; j++) {
                if (j % 5 == 0) {
                    System.out.println(j);
                    break lable;
                }
            }
        }
    }
}

数组

一维数组

  • 数组本身是引用数据类型,数组的元素可以是基本数据类型和引用数据类型
  • 数组的长度一旦确定便不能更改
  • 执行arr1 = arr2的操作时,只是将地址传递,并没有真的复制
//声明数组
//静态初始化
int[] score; or int score[];

//分配空间,指定数组能有多少空间
//数组是引用类型数据,实例化必须要使用new
score = new int[]{80, 50, 70, 95, 84}

//动态初始化
//或者声明的时候直接分配空间
int score[] = new int[5];

//获取数组长度
数组名.length

一维数组元素默认初始化值

数组元素类型默认初始化值
整形0
浮点型0.0
char型0而不是’0’,0代表null
boolean型false(实际上0代表false)
引用类型变量null

一维数组的内存解析

  • 栈(stack):存放局部变量
  • 堆(heap):new出来的结构,有对象、数组等,
  • 堆空间的每个结构都有个首地址值,通常用16进制来表示,将这个地址值赋给栈中的变量名
  • 堆空间先赋上元素的默认初始化值,在进行真正的赋值

二维数组

// 静态初始化
int[][] arr = new int[][]{{1, 2, 3}, {2, 4}}; // 跟python的list一样,每个一维数组可以不一样大

//动态初始化
int[][] arr1 = new int[3][2];

int[][] arr2 = new int[3][];//一维数组里面的数组大小不定的,使用前必须要先赋值

//获取数组长度
使用.length只能得到外面一维数组的长度

二维数组元素默认初始化值

int[][] arr = new int[3][2];
System.out.println(arr[0]); 
// 输出的是内层第一个一维数组的地址值
// output:[I@1b6d3586,表示int型的一维数组(有一个[)

//如果使用第二种动态定义的方法
int[][] arr = new int[3][];
System.out.println(arr[0]); 
// output:null,因为内层的一维数组还没有被定义,而一维数组是引用类型,元素默认参数是null,所以output为null
System.out.println(arr[0][0]);
// 报错,空指针异常

二维数组的内存解析

二维数组内存解析

Arrays工具类的使用

java.util.Arrays类是操作数组的工具类,包括操作数组的各种方法

命令作用
boolean equals(int[] a, int[] b);判断来个数组是否相等
String toString(int[] a);输出数组信息
void fill(int[] a, int val)将指定值全部替换到数组当中
void sort(int[] a)对数组进行排序
int binarySearch(int[] a, int key)数组a必须是排好序的,找到key的索引,如果没有返回负数

练习:对象数组

public class StudentTest{
    public static void main(String[] args){
        // 声明了Student类型的数组
        // 跟声明String类型的数组一样的!
        Student[] stus = new Student[20];
        for(int i = 0;i < stus.length;i++){
           stus[i] = new Student();
           //对每个成员变量进行赋值
           stus[i].number = i + 1;
           // Math.random()的范围是[0, 1)
           stus[i].state = (int)(Math.random() * 6 + 1);
           stus[i].score = (int)(Math.random() * 101);       
           
        }
    }
}

class Student{
    int number; // 学号
    int state; // 年级[1-6]
    int score; //分数[0-100]
}

面向对象

主线

  • Java类和类的成员:属性、方法、构造器;代码块,内部类
  • 三大特征:封装性、继承性、多态性、(抽象性)
  • 其他关键字:this、super、static、final、abstract、interface、package、import等

Java类和成员

  • 类是抽象的概念,是对象的模板
  • 对象是具体的事物,是类的具体实例
  • main函数要写在class中(好奇怪的说…)

类、对象的命名规范:

  • 类名首字母要大写,如果多个单词组成的类名,每个单词的首字母都要大写,不要以数字开头,不包含特殊字符
  • 对象首字母小写,后面单词首字母要大写,不要以数字开头,不包含特殊字符

实例化:

Dog doudou = new Dog();

匿名对象

// 不给new出来的对象赋予形参
new Phone().price = 1999;
// 只能用一次
// 但一般被传进别的函数,也是被赋予了形参
属性

也称为成员变量、field、域、字段,(可以在外面赋值…)

属性(成员变量) VS 局部变量

  1. 相同点:
  • 定义变量的格式:数据类型 变量名 = 变量值
  • 先声明后使用
  • 都有其作用的作用域
  1. 不同点:
  • 在类中声明的位置不同
    • 属性:直接定义在类的一对{}内
    • 局部变量:声明在方法内、方法形参、代码块内、构造器形参、构造器内部的变量
  • 关于权限修饰符的不同
    • 属性:可以在声明属性时,指明其权限,使用权限修饰符
    • 局部变量:不可以使用权限修饰符(其实用方法来代替了…)
  • 默认初始化值的情况
    • 属性:类的属性,根据其类型,都有默认的初始化值,跟数组的一样
    • 局部变量:没有默认初始化值,在使用之前一定要显式赋值
  • 在内存中加载的位置不同
    • 属性:加载到堆空间中(非static)
    • 局部变量:加载到栈空间中
public class HelloWorld{
    int age;
    String name;
    public static void main(String[] args){
        int a = 10;
        HelloWorld agent = new HelloWorld();
        agent.name = "tgk";
        agent.age = 18;        
    }
}
方法

也称为成员方法、method、函数

//四种例子
public void eat(){}
public void sleep(int hour){}
public String getName(){}
public String getNation(String nation){}
public class HelloWorld{

    //String是返回的数据类型,int是输入的数据类型

    public String isOddNumber(int num){
        if(num % 2 == 0)
            return "双数";
        else
            return "奇数";
    }
    // 主函数的入口!!!
    public static void main(String[] args){
        int a = 10;
        HelloWorld method = new HelloWorld();
        String str = method.isOddNumber(a);
        System.out.println(str);
    }
}

方法的重载

  • **定义:**在同一个类中,允许存在一个以上的同名方法,只要他们的参数个数或者参数类型不同即可

  • 跟方法的权限修饰符、返回值类型、形参变量名、方法体都没有关系

public class OverLoad {
    // 如下的四个方法都是构成了重载
    public void getSum(int i, int j){        
    }
    public void getSum(double i, double j){        
    }
    public void getSum(String i, int j){        
    }
    public void getSum(int i, String j){        
    }
}

可变个数的形参

// 从JDK5.0开始采用多个可变个数形参来定义方法,传入多个同一类型的变量
// 跟之前传递数组一样,通过遍历数组来获取值,如books.length
// 必须声明在末尾,即books必须在a后面
// 就跟python里的fun(*args)一样

public static void test(int a, String ... books);
// 与下面表示形式一样,就相当于一个数组
public static void test(int a, String[] books);
// 当与其他方法形参可以相同时,还是先以固定形参的方法为准!!!

变量的赋值(重点!)

  • 如果变量是基本数据类型,此时赋值的是变量所保存的数据值
  • 如果变量是引用数据类型,此时赋值的是变量所保存的数据的地址值

递归方法的使用(了解)

  • 递归方法,一个方法体内调用它自身
  • 方法递归包含了一种隐式的循环,会重复执行某段代码,但无需循环控制,递归一定要向已知方向递归,否则这种递归就变成了无穷递归,类似于死循环
public class RecursionTest {
    public static void main(String[] args) {
        System.out.println(new RecursionTest().getSum(100));
    }
	// 计算1~100的累加和,用递归
    public int getSum(int n){
        // 实际上这个if就是终止条件
        if(n == 1){
            return 1;
        }else{
            return n + getSum(n - 1);
        }
    }
}
构造器

作用

  • 创建对象
  • 初始化对象的属性

说明

  • 如果没有显式的定义类的构造器,则系统默认提供一个空参的构造器,权限跟类的权限相同
  • 定义构造器的格式:权限修饰符 类名(形参列表){ }
  • 一旦显式的定义了类的构造器之后,系统就不再提供默认的空参构造器
  • 一个类中,至少有一个构造器
public class HelloWorld{
    public static void main(String[] args){
        // 创建类的对象:new + 构造器
        Student stu = new Student(18);
        System.out.println(stu.number);
        }
}

class Student{
    // 属性
    int number;

    // 构造器,这个是默认的空的
    public Student(){
    }
    // 可以写多个,也称作重载
    public Student(int n){
        number = n;
    }

    // 方法
    public void eat(){
        System.out.println("吃饭");
    }
}
代码块
  • 用来初始化类、对象
  • 静态代码块
    • 内部类可以有输出语句
    • 随着类的加载而执行,只执行一次
    • 初始化类的信息
  • 非静态代码块
    • 内部类可以有输出语句
    • 随着对象的创建而执行,每创建一次对象都执行一次
    • 创建对象时,对对象的属性等进行初始化
  • 由父及子,静态先行
//静态代码块
static{
    System.out.println("静态代码块");
}
//非静态代码块
{
    System.out.println("非静态代码块");
}
内部类
  • Java中允许将一个类A声明在另一个类B中,则类A是内部类,类B称为外部类
  • 成员内部类(静态、非静态)和局部内部类(方法内、代码块内、构造器内)
  • 成员内部类
    • 作为外部类的成员—>调用外部类的结构,static修饰,四种权限修饰符修饰
    • 作为一个类—>类内定义属性、方法、构造器,final和abstract修饰
  • 实例化成员内部类的对象
// Dog和Brid是Person的两个内部类
// 创建Dog实例(静态的成员内部类)
Person.Dog dog = new Person.Dog();
dog.show();

//创建Bird实例(非静态的成员内部类)
Person p = new Person();
Person.Brid brid = p.new Brid();
bird.sing();
  • 开发中局部内部类的使用,就是在方法中新建类,通过接口,返回接口的一个实现类(外面不需要这个类)

三大特性

封装
功能
  • 隐藏功能的实现细节
  • 利用对象和方法是实现封装的直接途径
  • 良好的封装可以让代码更容易阅读和维护

体现

  • 将类的属性xxx私有化(private),同时,提供公共的(public)方法来获取(getXxx)和设置(setXxx)此属性的值
  • 不对外暴露的私有的方法
  • 单例模式

对成员变量进行读写封装

//先私有化成员变量
private String name = "tt";
//getter的书写格式为:public 成员变量类型 get成员变量名() PS:首字母大写
public String getName(){
    //this关键词可以不用
    return name;
}

//setter的书写格式为:public void set成员变量名(新的数据参数) PS:首字母大写
public void setName(String n) {
    (this.)name = n;
    //如果输入的名称和类中的不同,可以不用this
}
权限修饰符

访问修饰符用于控制类(只能用private和default)、成员变量、方法、构造器、内部类的访问范围

权限修饰符名称说明
private私有只能在类的内部访问
(default)默认相同包的其他位置可以访问
protected继承只有继承的子类能访问到
public公有在任何地方都能访问
继承
功能
  • 减少了代码的冗余,提高了代码的复用性
  • 便于功能的拓展
  • 为之后多态性的使用,提供了前提

说明

  • 子类、派生类、subclass
  • 父类、超类、基类、superclass
  • 一旦子类A继承父类B以后,子类A就获取了父类B中声明的所有的属性和方法,特别的,父类中声明为private的属性或方法,子类继承父类以后,仍然认为获取了父类中私有的结构,但因为封装性的影响,子类不能直接调用父类的结构
  • 子类继承以后,可以定义自己特有的属性和方法
  • 所有java类都直接或间接继承于java.lang.Object类
//extends关键词用于类的继承
//extends关键词后面是“父类”。前面是“子类”
public class Student extends Person //Student继承person
方法的重写
  • 子类继承父类之后,可以对父类中同名同参的方法进行覆盖操作(跟重载区别开!)
  • 重写之后,当创建子类对象调用重写的方法时,执行的是子类中重写的方法
  • 子类重写方法的权限不小于(>=)父类的权限,但是不能重写父类中为private的方法
  • 返回值类型要求
    • 父类被重写的返回值类型是void型,子类中重写的方法也只能是void型
    • 父类被重写的返回值类型是A类型,子类中重写的方法可以是A类或者A类的子类(引用数据类型)
    • 父类被重写的返回值类型是基本数据类型,子类中重写的方法必须是相同的基本数据类型
  • 子类重写的方法抛出的异常类型不大于(<=)父类被重写的方法抛出的异常类型
  • 子类父类中的同名同参的方法要么都声明为static(考虑重写),要么都声明为static(不是重写)
多态

一个事物的多种形态

理解

外接各种数据库的对象的时候,可以外接MySQLConnection和OracleConnection,在写方法时只需要写一个以父类Connetion对象为形参的方法,外面实际传递的时候可以传MySQLConnection或OracleConnection的对象,就没必要针对于不同的子类重写不同的方法!(真tm妙~)

说明

  • 使用前提

    • 类的继承关系
    • 方法的重写
  • 对象的多态性

    • 父类的引用指向子类的对象(或子类的对象赋给父类的引用)
    • 对象的多态性只适用于方法,不适用于属性(编译和运行都看左边)
  • 多态的使用

    • 虚拟方法调用,有了对象的多态性以后,在编译期,只能调用父类中声明的方法,但在运行期,实际上执行的是子类重写父类的方法(编译看左,运行看右
  • 多态是运行时行为,也称为动态绑定

// Person是父类,Student是子类
public class MulTest {
    public static void main(String[] args) {
        // 对象的多态性:父类的引用指向子类的对象
        Person p = new Student();
        // 多态的使用,当调用子父类同名同参的方式时,
        // 执行的是子类重写父类的方法 --虚拟方法调用
        p.eat();
        // p.study(); 会报错,只能调用Person类的方法
    }
}

其他关键字

this
  • this可以同来修饰属性、方法、构造器,通常情况下都省略了
  • this修饰属性和方法时可以理解为:当前对象
  • 显式的使用"this(形参列表)"方式,来调用本类中指定的其他构造器,必须放在首行
// 构造器,这个是默认的空的
public Student(){
	// Student初始化时需要考虑以下问题(假设好多代码)
    // 如果在每个构造器里都写,会冗余
}

public Student(int number){
    this(); // 直接调用空的构造器,上面初始化的代码只需要写一遍
    this.number = number;
}
super
  • 理解为:父类的,可以用来调用属性、方法、构造器
  • 可以在子类的方法或构造器中,通过使用"super.属性"或"super.方法"的方式,来显式的调用父类中的属性或方法
  • 可以在子类的构造器中显式的使用"super(形参列表)"的方式,调用父类中声明的构造器,必须放在首行
  • 在构造器的首行,没有显式的声明"super(形参列表)“或"this(形参列表)”,则默认调用父类中空参的构造器
public class Student extends Person{
    String major;
    public Student(){
        // 调用父类中空参的构造器
		super();
    }
    public Student(int age, String name, String major){
        // 直接调用父类中的构造器
        super(age, name);
        this.major = major;
    }
    public void eat(){
        // 调用了父类中的eat方法
        super.eat();
        System.out.println("学生应该吃有营养的食物!");
    }
}
  • **子类对象实例化的过程:**通过子类的构造器创建子类对象时,一定会直接或间接的调用其父类的构造器,仅为调用父类的父类的构造器…直到调用了java.lang.Object类中的空参构造器,因此才可以看到内存中有父类的结构,子类对象次啊可以调用
package

功能

  • 把功能相似或相关的类放到一个包里(就是一个文件夹)
  • 使用package声明类或接口所属的包,声明在源文件的首行
  • 包也采用了树形目录的存储形式,每"."一次,就代表一层文件目录

命名规则

  • 采用"逆域名法"进行命名,用"."分割,单词全部小写
  • 标准格式:域名后缀,组织机构名,项目名(模块名),包的职能
  • 同一个包下,不能命名同名的接口、类
例如:com.imooc.weather.ui
    
package com.imooc.demo.demo1; //声明下面的类是属于哪个包
import com.imooc.demo.demo2.Demo2; //导入别的包里面具体的类名

public class Demo1 {
    public static void main(String[] args) {
        Demo2 demo2 = new Demo2();
    }
}
import
  • 声明在包的声明和类的声明之间
  • 如果需要导入多个结构,可以并列写出
  • 可以使用"xxx.*"的方式,导入xxx包下的所有结构
  • 如果使用的类或接口是java.lang包下定义的,则可以省略import结构
  • 如果在源文件中使用了不同包下的同名的类,则必须至少有一个类需要以全类名的方式显示
  • import static:导入指定类或接口中的静态结构(属性、方法)
instanceof
// 对象的多态性:父类的引用指向子类的对象
Person p = new Student();
// p不能使用Student类的方法,可以使用向下转型,使用强制类型转换符
Student p1 = (Student) p;
// 此时p1就是Student类的对象了
// 使用强制转换是需要用instanceof来判断是否能转换成功!
  • a instanceof A:判断对象a是否时类A的实例,如果是,返回true,如果不是,返回false
  • 如果a instanceof A返回true,a instanceof B也返回true,则A和B是子父类的关系
static
  • static可以用来修饰:属性、方法、代码块、内部类
  • 用static修饰的属性称为静态变量(类变量)
    • 当创建了类的多个对象,多个对象共享同一个静态变量,一个对象改,其他都改
    • 静态变量随着类的加载而加载,可以通过"类.静态变量"的方式来调用,要早于对象的创建
    • 类只会加载一次,则静态变量在内存中也只会存在一份,存在方法区的静态域中
  • 用static修饰的方法称为静态方法(类方法)
    • 静态方法随着类的加载而加载,可以通过"类.静态方法"的方式来调用
    • 静态方法中,只能调用静态的方法或属性
  • 如何确定使用静态变量和静态方法
    • 属性是可以被多个对象所共享的;类中的常量也常常声明为static
    • 操作静态属性的方法,通常设置为static;工具类中的方法习惯上声明为static(Math、Arrays、Collections)
final
  • 可以修饰类、方法、变量(大写)
  • final修饰类:此类不能被其他类所继承,如String类、System类、StringBuffer类
  • final修饰方法:表明此方法不能被重写
  • final修饰变量:此时的“变量”就称为一个常量
    • final修饰属性:可以赋值的位置有:显式初始化、代码块中初始化、构造器中
    • final修饰局部变量:使用final修饰形参时,表明此形参是个常量,当调用此方法时,给常量形参赋一个实参,一旦赋值就只能在方法体内使用,但不能重新赋值
  • static final属性:全局常量
abstract
  • 当父类过于基础以后,就可以抽象成只表达类特性的类,不能创建对象,只能通过子类重写方法来实现功能

  • 修饰的结构:类、方法

  • abstract修饰类(抽象类)

    • 此类不能实例化
    • 一定有构造器,便于子类实例化的调用
  • abstract修饰方法(抽象方法)

    • 只有方法的声明,没有方法体
    • 包含抽象方法的类,一定是抽象类,反之,抽象类中可以没有抽象方法的
    • 若子类重写了父类中所有的抽象方法后,此子类才能实例化;若子类没有重写父类中所有的抽象方法,则此子类也是个抽象类,需要使用abstract修饰
public abstract String Person();// 没有方法体,必须保证类是抽象类,否则就能创建对象调用方法了
  • 注意点
    • 不能用来修饰属性、构造器等结构
    • 不能用来修饰私有方法、静态方法(子父类中同名同参的静态方法不认为是重写)、final方法、final的类
interface
  • Java中,接口和类是并列的两个结构
  • 定义接口中的成员
    • JDK7及以前:只能定义全局常量抽象方法
      • 全局常量:public static final,但是书写时可以省略不写
      • 抽象方法:public abstract
    • JDK8中,除了定义全局常量和抽象方法之外,还可以定义静态方法默认方法(default)
      • 接口中定义的静态方法,只能通过接口来调用
      • 用过实现类的对象,可以调用接口中的默认方法,如果实现类重写了接口中的默认方法,调用时仍然调用重写后的方法
      • 如果子类(或实现类)继承的父类和实现的接口中声明了同名同参的方法,那在子类没有重写此方法的情况下,默认调用的是父类中的同名同参的方法
      • 如果实现类实现了多个接口,多个接口中定义了同名同参的方法,实现类中没有重写此方法的情况下会报错----->接口冲突
      • 在子类(或实现类)的方法中调用父类、接口中被重写的方法:super.方法名;接口名.super.方法名
  • 接口中不能定义构造器!意味着接口不可以实例化!
  • Java开发中,接口通过让类去实现(implements)的方式来使用,如果实现类覆盖了接口中的所有抽象方法,则此实现类就可以实例化,否则此实现类仍然是抽象类
  • 子类中覆盖父类的方法叫重写,子类覆盖抽象父类或接口的方法叫做实现(康师傅,您真细~)
  • Java类可以实现多个接口—>弥补了Java单继承性的局限性
  • 接口与接口之间可以继承,而且是多继承
// 接口的例子
public class InterfaceTest{
    public static void main(String[] args) {
        System.out.println(Flyable.MAX_SPEED);
        System.out.println(Flyable.MIN_SPEED);
        Plane plane = new Plane();
        plane.fly();
    }
}

interface Attackable{
    void attack();
}

interface Flyable{
    // 全局常量
    public static final int MAX_SPEED = 7900;
    int MIN_SPEED = 1; // 省略了public static final

    // 抽象方法
    public abstract void fly();
    void stop(); // 省略了public abstract
}

class Plane implements Flyable{

    @Override
    public void fly() {
        System.out.println("通过引擎起飞");
    }

    @Override
    public void stop() {
        System.out.println("驾驶员减速停止");
    }
}

// 类可以实现多个接口
class Bullet extends Object implements Flyable, Attackable{

    // 要重写所有接口中的抽象方法才能创建对象
    @Override
    public void attack() {
    }
    @Override
    public void fly() {
    }
    @Override
    public void stop() {
    }
}

//实现接口和接口之间的多继承操作
interface AA{}
interface BB{}
interface CC extends AA,BB{}
  • 接口使用上也满足多态性
// 多态性的体现
public class USBTest {
    public static void main(String[] args) {
        
        // 四种接口的使用方法
        Computer computer = new Computer();
        // 1.创建了接口的非匿名实现类的非匿名对象
        Flash flash = new Flash();
        computer.transferData(flash);
        
        //2. 创建了接口的非匿名实现类的匿名对象
        computer.transferData(new Printer());
        
        // 3.创建了接口的匿名实现类的非匿名对象
        USB phone = new USB() {
            @Override
            public void start() {                
            }

            @Override
            public void stop() {
            }
        }; // 有大括号...
        computer.transferData(phone);
        
        // 4.创建了接口的匿名实现类的匿名对象
        computer.transferData(new USB() {
            @Override
            public void start() {                
            }

            @Override
            public void stop() {
            }
        });
    }
}

class Computer{
    public void transferData(USB usb){
        usb.start();
        System.out.println("具体数据传输的细节");
        usb.stop();
    }
}

interface USB{
    // 定义了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("打印机结束工作");
    }
}
  • 接口实际上就是定义
  • 开发中,体会面向接口编程

异常处理

  • 异常体系结构—>java.lang.Throwable

    • java.lang.Error:一般不编写针对性的代码进行处理
    • java.lang.Exception:可以进行异常的处理
      • 编译时异常(checked):IOException、ClassNotFoundException
      • 运行时异常(unchecked):NullPointerException、ArrayIndexOutOfBoundsException
  • 抓抛模型:

    • ”抛“:程序在正常执行的过程中,一旦出现异常,就会在异常代码处生成一个对应异常类的对象,并将此对象抛出,一旦抛出对象以后,其后的代码便不再执行
      • 系统自动生成的异常对象
      • 手动的生成一个异常对象,并抛出(throw)
    • ”抓“:可以理解为异常的处理方式,有try-catch-finally和throws
  • try-catch-finally使用

    • finally是可选的
    • 使用try将可能出现异常代码包装起来,一旦出现异常就会生成一个对应异常类的对象,通过catch对抛出的异常对象的类型进行匹配
    • 一旦满足处理完成,便跳出当前的try-catch结构(没有finally),继续执行其后面的代码
    • catch中的异常类型如果没有子父类的关系,则声明顺序无所谓,如果满足子父类关系,则子类一定声明在父类前面
    • 常用异常对象处理的方式:String getMessage()、printStackTrace()
    • try-catch-finally结构可以嵌套
  • finally使用

    • finally中声明的是一定会执行的代码,即使catch中又出现异常了,try中又return语句,catch中有return语句等情况,在try-catch结构结束之前执行
    • 像数据库连接、输入输出流、网络编程Socket等资源,JVM是不能自动回收的,需要自己手动的资源的释放,此时就要声明在finally当中
  • 体会:

    • 使用try-catch-finally处理编译时异常,使得程序在编译时就不再报错,但是运行时仍可能报错,相当于我们将一个编译时可能出现的异常延迟到运行时出现
    • 开发中,由于运行时异常比较常见,所以通常就不针对与运行时异常编写try-catch结构,对于编译时异常,要考虑~
    public class ExceptionTest {
        @Test
        public void test1(){
            String str = "123";
            str = "abc";
            try{
                int num = Integer.parseInt(str);
            }catch(NumberFormatException e){
                // System.out.println("出现数值转换异常了,莫慌...");
                // String getMessage()
                System.out.println(e.getMessage());
                // printStackTrace
                e.printStackTrace();
            }catch(NullPointerException e){
                // catch中又有异常会直接跳出去报错
                int[] arr = new int[10];
                System.out.println(arr[10]);
            }
            // finally代码一定会被执行
            finally {
                System.out.println("我好帅啊~~");
            }
        }
    }
    
  • throws + 异常类型

    • ”throws + 异常类型“写在方法的声明处,指明此方法执行时可能会抛出的异常类型,一旦方法体执行时出现异常,突然会在异常代码处生成一个异常类的对象,此对象满足throws后的异常类型时,就会被抛出,后续代码不会执行
  • **体会:**try-catch-finally:真正的将异常给处理掉了,throws的方式只是将异常抛给了方法的调用者,并没有将异常处理掉

如何选择try-catch-finally和throws?

  • 如果父类中被重写的方法没有throws方式处理异常,则子类重写的方法也不能用throws,意味着如果子类重写的方法中有异常,必须使用try-catch-finally方式去处理
  • 执行的方法a中,先后又调用了另外的几个方法,这几个方法时递进关系执行的,我们建议这几个方法用throws处理,而在执行的a中用try-catch方式处理

手动抛出异常

class Students{
    private int id;
    // 异常的处理,声明在方法处
    public void regist(int id) throws Exception {
        if(id > 0){
            this.id = id;
        }else
            // System.out.println("你输入的数据非法!");
            // 声明异常的对象,手动抛出异常
            throw new Exception("你输入的数据非法!");
    }
}

零碎知识点

== 和equals()的区别

== 运算符

  • 可以使用在基本数据类型变量和引用数据类型变量中
  • 如果比较的是基本数据类型变量,比较两个变量保存的数据是否相等(不一定类型要相同)
  • 如果比较的是引用数据类型变量,比较两个对象的地址值是否相同,即两个引用是否指向同一个对象实体

equals()方法

  • 是一个方法,而非运算符
  • 只能适用于引用数据类型
  • Object类中equals()和==的作用是相同的
  • 像String、Date、File、包装类等都重写了Object类中equals()方法,重写以后不再是比较两个对象的地址,而是比较两个对象实例属性是否相同

单元测试

此时单元测试方法,方法的权限是public,没返回值,没形参

idea暂时没看

包装类(Wrapper)

  • 针对八种基本数据类型定义相应的引用类型–包装类(封装类)
  • 有了类的特点,就可以调用类中的方法,Java才是真正的面向对象
  • 数值型基本数据类型的包装类都有父类Number
基本数据类型包装类
byteByte
shortShort
intInteger
longLong
floatFloat
doubleDouble
booleanBoolean
charCharacter

基本数据类型、包装类、String类型相互转化

import org.junit.Test;

public class WrapperTest {
    // 基本数据类型--->包装类
    @Test
    public void Test1(){
        int num = 10;
        // 调用包装类的构造器
        Integer int1 = new Integer(num);
        // 打印int1和int1.toString()一样
        System.out.println(int1.toString());
        // 也可以对只带数字的字符串创建对象
        Integer int2 = new Integer("123");
        System.out.println(int2);
    }

    // 包装类--->转换为基本数据类型
    @Test
    public void Test2(){
        Integer int1 = new Integer(12);
        // 调用包装类的xxxValue()
        int i1 = int1.intValue();
        System.out.println(i1);
    }

    // JDK 5.0新特性:自动装箱和自动拆箱
    @Test
    public void Test3(){
        // 自动装箱:基本数据类型--->包装类
        // 不用再调用包装类的构造器
        int num = 10;
        Integer int1 = num;

        // 自动拆箱:包装类--->转换为基本数据类型
        int num1 = int1;
    }

    // 基本数据类型、包装类--->String类型
    @Test
    public void Test4(){
        int num = 10;
        // 方式1:连接运算
        String str1 = num + "";
        // 方式2:调用String重载的valueOf(Xxx xxx)
        float f = 12.3f;
        String str2 = String.valueOf(f);// "12.3"
    }

    // String类型--->基本数据类型、包装类
    @Test
    public void Test5(){
        String str1 = "123";
        // 调用包装类的parseXxx()
        int num = Integer.parseInt(str1);
        System.out.println(num + 1);
    }
}

单例设计模式

  • 就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例
  • 两种模式:饿汉式和懒汉式
import org.junit.Test;

public class Singleton {
    @Test
    public void SingletonTest1(){
        Bank bank = Bank.getInstance();
    }

    @Test
    public void SingletonTest2(){
        Order order = Order.getInstance();
    }
}

// 饿汉式
class Bank {
    // 1.私有化类的构造器
    private Bank(){
    }

    // 2.内部创建类的对象,也可以直接理解为类的属性(一个对象)
    // 4.要求此对象也必须声明为静态的
    private static Bank instance = new Bank();

    // 3.提供公共的静态的方法,返回类的对象
    public static Bank getInstance(){
        return instance;
    }
}

// 懒汉式
class Order{
    // 1.私有化类的构造器
    private Order(){
    }

    // 2.声明当前类对象,没有初始化
    // 4.此对象也必须声明为static
    private static Order instance = null;

    // 3.提供公共的静态的方法,返回类的对象
    public static Order getInstance(){
        if(instance == null){
            instance = new Order();
        }
        return instance;
    }
}
  • 区分饿汉式和懒汉式
    • 饿汉式:好处:饿汉式是线程安全的;坏处:对象加载时间过长
    • 懒汉式:好处:延迟对象的创建;坏处:目前来说是线程不安全的

懒汉式线程安全的写法

class Order{
    private Order(){
    }

    private static Order instance = null;
    public static Order getInstance(){
        // 懒汉式线程安全的写法
        // 方式1:效率较差
//        synchronized(Order.class) {
//            if (instance == null) {
//                instance = new Order();
//            }
//            return instance;
//        }
        
        // 方式2:效率稍高
        // 单例模式加锁需要两个if判断
        if(instance == null) {
            synchronized (Order.class) {
                if (instance == null) {
                    instance = new Order();
                }
                return instance;
            }
        }
        return instance;
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值