Java面向对象

123 阅读9分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第26天,点击查看活动详情

三大特征

封装、继承、多态

方法的调用

静态方法、非静态方法

静态方法可以调用静态方法,非静态调用非静态,但不能混合调用

例:可以如下静态方法间调用

public static void a(){
b();
}
public static void b(){
}

但不可以静态与非静态混合调用

public void a(){
b();
}
public static void b(){
}

形参和实参

实参
add(1,2);
形参
add(int a,int b){
  return a+b;}

值传递和引用传递(oop.demo1.Test1-Test3)

值传递

Test1

传递对象的一个副本,即使副本被改变,也不会影响源对象,因为值传递的时候,实际上是将实参的值赋值一份给形参

引用传递

Test2实例,Test3反例

传递的并不是实际的对象,而是对象的引用,外部对引用对象的改变也会反映到源对象上,因为引用传递的时候,实际上是将实参的地址值赋值一份给形参

创建类及初始化对象

使用new关键词创建对象

快捷键:new 对象.var

*构造器

public class Person(){
  public Person(){
  }
}

规则

  • 必须和类名相同
  • 必须没有返回值类型,也不能写void
  • 实例化子类时会先调用父类的构造器,在调用子类的构造器,如果父类无法调用无参构造器,就需要子类通过super来调用有参构造器

作用

  • 使用new关键词,本质是在调用构造器
  • 初始化值

注意点

  • 无参构造即使不定义也默认存在,但定义有参构造之后,如果再想使用无参构造,需提前定义一个无参的构造
  • Alt+Insert 可快捷生成构造器

封装(oop.demo2.FengZhuang)

属性私有,get/set

在属性设为private私有后,实例化后是无法访问该私有属性的

可以设置get,set方法来获取和设置该属性(Alt+Ins)

继承(oop.demo3)

通过final修饰后的类不能继承

extends(Student、Person、Application)

子类继承父类,就会拥有该父类的所有公有方法 (子 extends 父)

ctrl+H 继承树(默认继承object类)

java中只有单继承,不能有多继承(一个儿子一个爸爸,一个爸爸可以有多个儿子)

 

super(Student1、Person1、Application1)

super与this相对,this输出当前对象属性,super是父类对象的

注意点

  • super调用父类的构造方法,必须在构造方法的第一个
  • super必须只能出现在子类的方法或者构造方法中
  • super和this不能同时调用构造方法 (构造方法必须在第一行,都调用必然会有一个不在第一行)

前提

this:没有继承类也可以用

super:只能在继承条件下才能使用

构造方法

this():本类的构造方法

super():父类的构造方法

用法

super.print(); //调用父类的方法
System.out.println(super.name); //输出父类的name属性值
super()、this() //直接调用父类、子类的构造器

 

方法重写(A、B、Applicaition2)

方法覆盖就是子类有一个方法和父类的某个方法的名称、返回类型、参数一样,那我们就说子类的方法重写了父类的方法

规则

  • 方法名必须相同
  • 参数列表必须相同
  • 修饰符:方位可以扩大但不能缩小: public > protect > default > private
  • 抛出的异常:范围,可以被缩小,但不能扩大:ClassNotFoundException -->

Exception(大)

  • 子类的方法和父类必须一直,方法体不同

为什么需要重写

父类的功能,子类不一定需要,或者不一定满足

Alt + Insert:override

静态与非静态方法区别

静态方法:方法的调用只和左边定义的数据类型有关

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


//结果:
A=>test()
B=>test()

 

非静态方法:重写

运行上述代码结果:
A=>test()
A=>test()

重写后有如下标志

多态(oop.demo4)

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

条件(Student、Person、Application)

  • 有继承关系
  • 子类重写父类方法
  • 父类引用指向子类对象 -> Father f1=new Son();

注意点

  • 多态是方法的多态,属性没有多态
  • 父类和子类要有联系 例:Student类也可用Object类表示
  • static方法、final常量、private方法不能用多态

规则

设 Student 子类 Person父类
  • Student 能调用的方法都是自己或者继承父类Person的方法
  • Person父类可以指向Student子类,但不能调用子类特有的方法
  • 子类重写了父类的方法,会执行子类的方法

实例1:

当Student中没有run方法,执行student.run();时会调用 父类Person中的run方法输出run

当Student中有run方法后,方法重写则会执行子类Student中的run方法,输出son

 

实例2:

package Sentiment.array;
class Person5{
    public String name;
    public Person5(){
        name="person";
    }
    public String getName() {
        return name;
    }
}
class Student5 extends Person5 {
    public String name;
    public Student5(){
        name="Student";
    }
    public String getName() {
        return name;
    }
}
public class test{
    public static void main(String[] args) {
        Person5 person5 = new Student5();
        System.out.println(person5.name); //结果 person
        System.out.println(person5.getName());//结果 Student
    }
}

原因:子类重写了父类的方法getName(),会执行子类的方法getName(),但属性name不会

Instanceof(Student、Person、Teacher、Application1)

Student、Teacher是Person的子类,Application1用于测试

定义

测试左边的对象是否是它右边的类的实例,返回 boolean 的数据类型。

个人理解

看了很多资料只是表示了左边的对象是否是它右边的类的实例 的一个概念,但并未标明false和编译报错之间的关系,于是记录下个人理解 (仅供参考)

true

没啥好说的,左边的对象是它右边的类的实例

false

对象y不是类z的实例,但xz之间存在父子类关系

编译报错

对象y不是类z的实例,且xz之间不存在父子类关系

 

注意点

  • 把子类转换为父类,即向上转型不需要类型转换
//编译出错
Student a = new Person(); 
//正常执行
Person a = new Person();
Person a = new Student();
  • 把父类转换为子类,即向下转型需要进行类型转换
Student a = new Person();  
//上述会编译出错,无法将父类转换为子类,所以需要类型转换
Student b = (Student) a;
  • 父类引用指向子类的对象 ,子类引用不可以指向父类对象
Person a = b  //父类引用a指向子类对象b

static关键字(oop.demo5)

用法(Student)

当age设为静态变量后可以通过类直接调用,而age则编译出错;再实例化后都可用对象调用,而静态变量age也可以直接输出

匿名代码块(Person)

在对象实例化后会先调用静态代码块,然后调用匿名代码块,随后在调用构造器

{
  //匿名代码块
}

静态代码块

静态代码块只调用一次

{
  //静态代码块
}

静态导入包(Test)

正常情况下直接调用数学函数会报错

但通过静态导入包即import static xxx.xxxx.xxxx 后即可直接调用

抽象类(oop.demo6)

只有方法名,没有方法的实现

public abstract 类型 方法();

Alt+Inser 实现方法

注意点

  • abstract修饰符可以用来修饰方法也可以修饰类
  • 不能new抽象类,只能靠子类去实现
  • 抽象类中可以写普通的方法,抽象方法必须写在抽象类中
  • 子类继承抽象类,就必须实现抽象类中的抽象方法,否则需要声明为抽象类

接口(oop.demo7)

普通类:只有具体实现

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

接口:只有规范,自己无法写方法

接口可以多继承,extends只能单继承

作用

  • 定义一些方法,让不同人实现(多个人可以同时实现一个接口)
  • 方法定义:pulic abstract 常量定义:public static final
  • 接口不能被实例化,接口中没有构造方法
  • implements可以实现多个接口
  • 必须要重写接口中的方法

内部类(oop.demo8)

成员内部类

在类中,新建个类

静态内部类

加上static即可,底层优先执行

局部内部类

pulic类下,创建的类都是局部内部类,一个java只能有一个public类

匿名内部类

可以对接口进行实例化

异常

Throwable类

java把异常当做对象来处理,java.lang.Throwable作为所有异常的超类

分类

Error

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

Exception

运行时异常

  • RuntimeException(运行时异常)

非运行时异常

  • ArrayIndexOutOfBoundsException(数组下标越界)
  • NullPointerException(空指针异常)
  • ArithmeticException(算术异常)
  • MissingResourceException(丢失资源)
  • ClassNotFoundException(找不到类)

这些异常是不检查异常,程序中可以选择捕获处理,也可以不处理。

区别

  • Error通常是灾难性的致命的错误,是程序无法控制和处理的,当出现这些异常时,Java虚拟机(JVM)一般会选择终止线程。
  • Exception通常情况下是可以被程序处理的,并且在程序中应该尽可能的去处理这些异常。

异常处理机制

捕获异常

  • try catch 结构
  • try catch finally结构

在执行a/b时,由于0不能作为除数,即报出ArithmeticException异常

public class Test {
    public static void main(String[] args) {
        int a=1;
        int b=0;
        System.out.println(a/b);
    }
}

 

根据ArithmeticException类型进行异常捕获,快捷键:Ctrl+Alt+T

public class Test {
    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("算数异常");
        } finally {                             //finally 关闭捕获防止资源占用
            System.out.println("finally");
        }
    }
}
​
//结果:
算术异常
finally

若需要多个捕获异常则异常需要从小到大排序Throwable > Exception = Error > ArithmeticException……,如下若Throwable、Exception互换则会编译出错

public class Test {
    public static void main(String[] args) {
        int a=1;
        int b=0;
        try { //try监控区域
            System.out.println(a/b);
        } catch (Error e) { //catch(想要捕获的异常类型)
            System.out.println("Error");
        } catch (Exception e){
            System.out.println("Exception");
        } catch (Throwable e){
            System.out.println("Throwable");
        }
    }
}
​
//结果
Exception

 

抛出异常

throw

  • throw用在方法内

throws

  • throws用在方法声明后
public class Test {
    public static void main(String[] args) {
    new Test().test(1,0); //匿名内部类
​
    }
    public void test(int a,int b) {
        if(b==0){
            throw new ArithmeticException(); //throw方法内抛出异常
        }
    }
}
​

 

throws在方法上抛出异常,使用时一般需要异常捕获

public class Test {
    public static void main(String[] args) {
        try {
            new Test().test(1,0); //匿名内部类
        } catch (ArithmeticException e) {
            e.printStackTrace();
        }
​
    }
    public void test(int a,int b) throws ArithmeticException{
        if(b==0){
            throw new ArithmeticException(); //throw方法内抛出异常
        }
    }
}

自定义异常

自定义异常必须继承Exception类或RuntimeException类

public class MyException {
    public static void main(String[] args) {
        new MyException().test(110);
    }
    
    public void test(int a){
        if(a>120||a<10){
            throw new AgeException("年龄需要在10-120之间");
        }
        System.out.println("输入正确");
    }
}
class AgeException  extends RuntimeException{
    public AgeException(String message) {
        super(message);
    }
​
}

一般情况下,继承RuntimeException类,因为可以使用默认的处理机制,若继承Exception则需要再用throws抛出,即:

public class MyException {
    public static void main(String[] args) throws AgeException {
        new MyException().test(110);
    }
    
    public void test(int a){
        if(a>120||a<10){
            throw new AgeException("年龄需要在10-120之间");
        }
        System.out.println("输入正确");
    }
}
class AgeException  extends Exception{
    public AgeException(String message) {
        super(message);
    }
}