Java Day10

一、函数式接口

  1. @Funcitonallnterface (新版本)

    加上改注解,编译器会默认这个类是一个函数式接口,只能存在一个类方法,如果存在两个方法编译器会报错

    下面编译器会报错

    1
    2
    3
    4
    5
    @FunctionalInterface
    interface One_method{
    public abstract void Pt();
    // public void p(); 不能声明第二方法
    }

二、匿名内部类

  1. 本质上还是一个类
  2. 是一个内部类
  3. 该类没有名字(编译器会给该类一个代号)
  • 传统思路,一个普通类继承了接口类,需要实例化对象我们才能调用接口中的抽象方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    public class Main implements InterFace{
    @Override
    public void Prt(){
    System.out.println("重写了函数式接口的方法");
    }

    public static void main(String[] args) {
    //传统思路 用类-》实现抽象类中方法-》实例化类并且调用方法
    InterFace m = new Main();
    m.Prt();
    }

    }

    @FunctionalInterface
    interface InterFace{
    public abstract void Prt();
    }
  • 当我们只是想单纯调用接口中的Prt方法,上述方法会显得非常冗余

​ 此时我们就可以采用匿名类,来简化上述代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//匿名内部类
@FunctionalInterface
interface InterFace{
public abstract void Prt();

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

//这里并没有实例化,用到了匿名类
new InterFace(){
@Override
public void Prt(){
System.out.println("helleworld");
}
}.Prt();
}
}
  • 多内部中多可方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
//匿名内部类
//@FunctionalInterface
public interface InterFace{
public abstract void Prt();
public abstract void Prt2();

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

//这里并没有实例化,用到了匿名类
InterFace a = new InterFace(){
@Override
public void Prt(){
System.out.println("helleworld");
}
@Override
public void Prt2(){
System.out.println("只是第二方法");
}
};

a.Prt();
a.Prt2();
// 我可以是用getClass() 方法获取这个类名
System.out.println(a.getClass());
}
}

虽然匿名内部类没有名字, 但系统会给我自动生产一个类名
上述是在Main内种的内部类 所以该匿名内部类中的名字为class Main$1 如果还有第二个那么他的名字为Main$2


注意事项:

1、使用匿名内部类时,我们必须是继承一个类或者实现一个接口,但是两者不可兼得,同时也只能继承一个类或者实现一个接口。

2、匿名内部类中是不能定义构造函数的。

3、匿名内部类中不能存在任何的静态成员变量和静态方法。

4、匿名内部类为局部内部类,所以局部内部类的所有限制同样对匿名内部类生效。

5、匿名内部类不能是抽象的,它必须要实现继承的类或者实现的接口的所有抽象方法。

匿名内部类使用的最佳环境,当一个函数需要传入一个类时,并且只使用了该类的极少的方法,我们可以使用这种方法来简化。

小练习

数据库链接 oracle 、mysql

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
import java.util.Scanner;

public interface IDAO {
public abstract void add();
public abstract void del();
public abstract void upd();
public abstract void sel();
public abstract void connect();

public static void main(String[] args) {
Scanner i = new Scanner(System.in);
System.out.println("请输入你要链接的数据库 1.mysql 2.oracle");
int n = i.nextInt();

IDAO idao;
if (n == 1){
idao = new MySql();
idao.connect();
}else {
idao = new Oracle();
idao.connect();
}
}
}

abstract class AbstractDao implements IDAO{
@Override
public void add() {
System.out.println("增加数据");
}

@Override
public void del() {
System.out.println("删除数据");
}

@Override
public void upd() {
System.out.println("更新数据");
}

@Override
public void sel() {
System.out.println("更改数据");
}

}

class MySql extends AbstractDao{
@Override
public void connect() {
System.out.println("链接Mysql数据库");
}
}

class Oracle extends AbstractDao{
@Override
public void connect() {
System.out.println("链接Oracle数据库");
}
}

三、成员变量的默认值

  1. Java、c、c++ 这类语言中局部变量声明定义后必须要初始化,否则会出错

    1
    2
    3
    4
    5
    6
    7
    8
    public class Init{
    public static void main(String[] args) {
    int i;
    System.out.println(i);
    }
    }
    ---
    java: 可能尚未初始化变量i
  2. 在类中,你为给定其值时编译器会自动给你赋值,(java类 c结构体 c++类 中都有这类特性)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    public class Init{
    int a;
    short b;
    long c;
    String d;
    boolean e;
    char f;
    double g;
    float h;
    Chlid i;

    public static void main(String[] args) {
    //默认值
    Init a = new Init();
    System.out.println(a.a);
    System.out.println(a.b);
    System.out.println(a.c);
    System.out.println(a.d);
    System.out.println(a.e);
    System.out.println(a.f);
    System.out.println(a.g);
    System.out.println(a.i);
    System.out.println(a.i);

    }
    }

    class Chlid{

    }

    上述展示了大部分成员变量的默认值

四、object

1.object 是一个类

所有类默认继承了object,你定义一个空类,你会发现,类可以调用一些你从来没有定义过的方法

2.equals方法 、== 的区别

  • equals 是object 提供的判断一个对象是否等于另一个对象

  • == 用于判断两个内存地址的值是否相等

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    public class Equals {


    public static void main(String[] args) {
    String a = "2333";
    String b = "2333";
    System.out.println(a == b);
    System.out.println(a.equals(b));

    }
    }
    ---
    true
    true
  • 两种判断是不一样的

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    public class Equals {


    public static void main(String[] args) {
    String a = new String("2333");
    String b = new String("2333");
    System.out.println(a == b);
    System.out.println(a.equals(b));

    }
    }
    ---
    false
    true

    a b 他们值都相同,但是使用equals和==判断会得到两种不同结果呢?

    如果熟悉其他编程语言,对于这个问题可能理解的很快

在程序里最常用的数据类型是字符串,如果有大量相同的字符串会极大的占据空间,那么我们可以只开辟一个字符串的空间,让其他相同值的变量指向同一块内存地址,从而优化内存空间

  • 1中 两个a b 都是同一个内存地址所以== 和 equals判断都为true

  • 2中 注意我们使用了new 开辟了两块空间让对象a,b分别指向,又因为相同字符串在内存中只会存储一份a 和b的实际字符串数值还是会指向 内存中同一块地址

    • 所以对象a 和 对象b 本身是两个不同地址的对象 == 只判断其地址相等与否 所以为false
    • 而equals 判断的是其值得地址是否相等 所以为true

3.判断类是否相等

显然用== 判断 是不行的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//定义一个猫类  
public class Cat {
String name;
int age;

public Cat(String name, int age) {
this.name = name;
this.age = age;
}

public static void main(String[] args) {
Cat a = new Cat("小黑", 10);
Cat b = new Cat("小黑", 10);
//判断其颜色是否相等
System.out.println(a == b);
System.out.println(a.equals(b));
}

}
---
false
  • 重构object 中的equals 自定义条件来判断
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
//定义一个猫类  
public class Cat {
String name;
int age;

public Cat(String name, int age) {
this.name = name;
this.age = age;
}

public static void main(String[] args) {
Cat a = new Cat("小黑", 10);
Cat b = new Cat("小黑", 10);

System.out.println(a.equals(b));
}
//重写了equals方法
//下面是为了熟练 继承多态 大可不必这么写
@Override//多余
public boolean equals(Object obj) {//Object obj 改为 Cat
//这个按断就不要这么写了 只是用加深理解 this.name = obj.name
if (this.name.equals( ( (Cat)obj ).name) )
return true;
else
return false;
}
}

总结:

在对字符串进行比较时候我们要使用object 提供的 equals() 来比较

基础数据类型用 ==

4.toString方法

打印输出类的相关信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package com.aaa.bbb;

public class Person {
String name;
int age;


public Person(String name, int age) {
this.name = name;
this.age = age;
}

public static void main(String[] args) {
Person a = new Person("小张", 21);

System.out.println(a);
System.out.println(a.toString());

}

}
---
com.aaa.bbb.Person@7ef20235
com.aaa.bbb.Person@7ef20235

默认 包+类 @内存地址

  • 显然这种输出我们是不满意的,我可以重写该方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    package com.aaa.bbb;

    public class Person {
    String name;
    int age;


    public Person(String name, int age) {
    this.name = name;
    this.age = age;
    }

    public static void main(String[] args) {
    Person a = new Person("小张", 21);

    System.out.println(a);
    System.out.println(a.toString());

    }
    //idea 自动生成重写的tostring()方法
    @Override
    public String toString() {
    return "Person{" +
    "name='" + name + '\'' +
    ", age=" + age +
    '}';
    }
    }
    ---
    Person{name='小张', age=21}
    Person{name='小张', age=21}

五、instanceof 关键字

判断一个对象是不是否是某个对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class Animal{

}

public class Cat extends Animal {
public static void main(String[] args) {
Animal ani = new Cat();

Animal ani2 = new Animal();
//判断ani是否是一只猫
if (ani instanceof Animal)
System.out.println("这是动物");
if (ani instanceof Cat)
System.out.println("这是一只猫");

if (ani2 instanceof Animal)
System.out.println("这是动物");
if (ani2 instanceof Cat)
System.out.println("这是一只猫");
else
System.out.println("但不是猫");
}
}


六、参数传递

对于参数传递,如果是第一次了解,确实抽象。好在我之前有学过C++ 理解起来就很容易,一下是自己的一些见解

首先对于参数传递问题,我们可以了解下堆栈的机制和块作用域,我就拿块作用域来说吧,{ } ( ) 括号中的代码就是一个块作用域,在括号里面声明的变量,一旦运行结束 里面声明的变量什么的都会被销毁(栈回收,类会随着程序结束而回收),也就是我们称作的局部变量,它的什么周期只限于在他运行时候。理解完 下面的就会容易很多

1.值传递

基础类型传递,来看看下面代码

  • 一个基础的int型 变量
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Demo01 {
public static void change(int i){
i = 20;
System.out.println(i);
}
public static void main(String[] args) {
int i = 10;
change(i);
System.out.println(i);
}
}
---
20
10

我们发现i在change 方法里是20 但是main方法里的i还是10

因为java是值传递 i这个变量的值原本为10 传入 到change 形参i2 这个形参是只作用于chage 这个方法内,他和这个块外的i不是同一个变量,只是值相同罢了。

所以chage方法内 赋值的20 一旦chage运行结束这个i2 变量就消失了


  • 实例化了一个有两个成员变量的对象,并改变了提供的方法中改变了其值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class Demo01 {
String s;
int i;

public Demo01(String s, int i) {
this.s = s;
this.i = i;
}

public static void change(Demo01 n){
n = new Demo01("333", 20);
}

public static void main(String[] args) {
Demo01 n = new Demo01("123", 10);

n.change(n);
System.out.println(n.i);
System.out.println(n.s);
}
}
---
10
123

这个很好理解,形参n是先将n的值获取了 chage方法内使用new 为 n 在堆中开辟了另一块区域 但是这个新的区域也会随着 change生命的结束 而被回收

2.引用传递 — (地址传递)

引用传递,Java传递本质是依然是值传递,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class Demo01 {
String s;
int i;

public Demo01(String s, int i) {
this.s = s;
this.i = i;
}

public static void change(Demo01 n1){
n1.i = 20;
n1.s = "321";
}

public static void main(String[] args) {
Demo01 n = new Demo01("123", 10);

n.change(n);
System.out.println(n.i);
System.out.println(n.s);
}
}
---
20
321

我们发现值改变了,为什么呢?

形参是对象时,这个对象和基础数据类型,是不一样的。对象中的值为指向堆中的地址,而基本数据类型的值就是他本身,所以一个是地址一个是值,他们都是值。。。

  • 基础数据类型传入的是基础数据类型本身的值;
  • 而对象传入时,对象本身的值是指向其在堆空间地址

明白上述两点,我们传入了n这个对象的地址, 通过n1这个新的变量来访问这片空间,并且改变了这个空间中的值。随着change函数的结束n1这个变量被销毁了。但是n这个变量,因为主函数main还在运行,所以依旧存在。

总结:知道java是值传递就行了。。。

七、异常处理

编译时异常,自己改、

运行时出现错误

1
2
3
4
5
6
7
8
public class Demo01 {
public static void main(String[] args) {
System.out.println(1/0);
}
}
---
Exception in thread "main" java.lang.ArithmeticException: / by zero
at Demo01.main(Demo01.java:3)
  1. 异常错误,运行时异常
  2. 抛出异常列,创建一个异常对象,并把对象抛出
  3. 补货异常对象,默认jvm来处理错误并把不能处理的错误打印出来,jvm会让程序停止

1.异常的分类

异常是一个对象因此也会有各种异常还继承关系:

  • Throwable (父类)
    • Error
    • Exception
      • RuntimeException
        • 一堆Exception
      • 其他的各类Exception

Error:一般是系统级的错误,并且比较难处理

我最常解决的是Exception的错误类抛出

其他各类Exception:必须处理才能编译运行 编译时异常

RuntimeException:是程序跑起来运行中的错误 运行时异常

2.异常处理

try..catch

因为jvm来处理会停止程序,但是我们依然想让程序继续运行下去,我就要自己处理异常,跳过jvm

try{

​ 容易出错的代码

}catch(出错类 e){

​ 处理异常的代码

}finally{

​ 最终无论执行过try 还是catch

}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Demo01 {
public static void main(String[] args) {
try{
System.out.println(1/0);
}catch (Exception e){
e.printStackTrace();//打印错误信息
System.out.println("系统炸了");
}finally{
System.out.println("我啥都接,不管你是否出错也是否解决");
}

System.out.println("helloworld");
}
}

throws 错误类型{}

表示方法可能抛出一个异常

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;

public class Demo01 {

public static void read() throws Exception{
InputStream f = new FileInputStream(new File("文件地址"));
}

public static void main(String[] args) {
try {
read();
}catch (Exception e){

}
}
}


read()方法可能会产生一个错误,向上抛出一个错误,我们可以用try catch 解决

throw

主动向外抛出一个异常

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Demo01 {

public static void read(int i ) throws Exception{
if (i == 0){//主动抛出
throw new Exception("错误描述");
}
}

public static void main(String[] args) {
try {
read(1);
}catch (Exception e){

}
}
}

自定义异常

自定义异常类必须成Exception或者RuntimeException

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class AutoException extends Exception{
public AutoException(String message) {
super(message);
System.out.println("自定义错误描述");
} //自定义异常必须继承Exception或者RuntimeException

}


class Room{
int Person_4 = 3;

public void setPerson(int person) throws AutoException{
if (4 >= person) {
this.Person_4 = person;
}else {
throw new AutoException("这间房只能4个人住");
}
}

public static void main(String[] args) {
Room a = new Room();
try {
a.setPerson(6);
} catch (AutoException e) {
System.out.println(e.getMessage());
}
}
}