面向对象编程

一、初识面向对象

二、方法回顾与加深

break和return的区别

​ break:跳出switch,结束循环;

​ return:结束方法,返回内容(与返回类型进行对应,viod可以返回空)。

public class Demo02 {
    public static void main(String[] args) {
        //静态方法 调用
        Student.say();

        //非静态方法(需要实例化这个对象new)
        Student student = new Student();
        student.play();
    }
    // static时和类一起加载的,但是不加static是与对象有关的,需要类实例化之后才存在
    // 两个方法同为static,或者同无static才可以互相调用,否则不可以互相调用
    public void a(){
        b();
    }
    public  void b(){
        a();
    }

    public static void c(){
        d();
    }
    public static void d(){
        c();
    }
}

//值传递
public class Demo04 {
    public static void main(String[] args) {
        int a=1;
        System.out.println(a);//1
        change(a);
        System.out.println(a);//1,这就是值传递,change方法改变的值只是形参,并没有返回,所以main方法中的a,不会改变
    }
    public static void  change(int a){
        a=10;
    }
}

//引用传递:传的是对象,本质还是值传递
public class Demo05 {
    public static void main(String[] args) {
        Person person = new Person();
        System.out.println(person.name);//null
        change(person);
        System.out.println(person.name);//荔枝,这就是引用传递
    }
    public static void  change(Person person){
        person.name="荔枝";//这里修改的是person类里面的name值,person是一个对象,指向:Person person = new Person();
    }
}
//定义了一个Persong类,有一个属性:name
class Person{
    String name;
}

三、对象的创建分析

public class Student {
    String name;//null
    int age;//0


    public void  study(){
        System.out.println(this.name+"在学习");
    }
}


public class Application {
    public static void main(String[] args) {
        //类:抽象的,需要实例化
        //类实例化后会返回一个自己的对象
        //student对象就是一个Student类的具体实例

        Student xiaoMing = new Student();
        Student xiaoHong = new Student();

        xiaoMing.name="小明";
        xiaoMing.age=3;

        System.out.println( xiaoMing.name);
        System.out.println( xiaoMing.age);

        xiaoHong.name="小红";
        xiaoHong.age=5;

        System.out.println( xiaoHong.name);
        System.out.println( xiaoHong.age);

    }
}




public class Person {
    //一个类什么都不写,它也会存在一个方法
    //显示定义构造器
    String name;

    //实例化初始值
    //1.使用new关键字,本质是在调用构造器
    //2.构造器用来初始化值
    public Person(){//无参构造

    }
    // 一旦定义了有参构造,想通过无参构造new对象,那么无参构造就必须显示定义
    public Person(String name){//有参构造
        this.name=name;
    }
    //快捷键  alt+insert 选择第一个constructor创建构造器

    /**
     *  public static void main(String[] args) {
     *         Person person = new Person("荔枝");
     *         System.out.println(person.name);
     *     }
     */

    
}

构造器:
1.和类名相同
2.没有返回值

作用:
1.new 本质在调用构造方法
2.初始化对象的值

注意点:
1.定义有参构造之后,如果想使用无参构造,显示的定义一个无参的构造
Alt + Insert 快捷键

package com.oop.demo03;

public class Pet {
    public String name;
    public int age;

    public void  shout(){
        System.out.println("叫了一声");
    }

    /*
    public static void main(String[] args) {
        Pet dog = new Pet();
        dog.name="旺财";
        dog.age=3;
        dog.shout();

        System.out.println(dog.name);
        System.out.println(dog.age);

        Pet cat = new Pet();
    }
     */
}

对应的内存图

小结:

  1. 类与对象
    类是一个模板:抽象,对象是一个具体的实例
  2. 方法——定义、调用!
  3. 对应的引用
    引用类型 :基本类型(8)
    对象是通过引用来操作的:栈--->堆
  4. 属性:字段Field 成员变量
    默认初始化: 数字:0 0.0 char :u0000 boolean:false 引用:null
    定义:修饰符 属性类型 属性名=属性值!
  5. 对象的创建和使用
    必须使用new 关键字创造对象,构造器 Person kuangshen= new Person();
    对象的属性kuangshen.name
    对象的方法kuangshen.sleep()
  6. 类(静态的属性——属性、动态的行为——方法)

四、面向对象三大特性★★★★★

封装

//封装
/**
 * 1.提高程序的安全性,保护数据
 * 2.隐藏代码的实现细节
 * 3.统一接口
 * 4.系统的可维护性增加
 */
public class Student {
    // 属性私有
    private  String name;//姓名
    private  int id;//学号
    private  char sex;//性别
    private  int age;

    //提供一些可以操作这个属性的方法
    // 提供一些public的get、set方法

    //get获取值

    public String getName() {
        return name;
    }
    //set设置值
    public void setName(String name) {
        this.name = name;
    }

    public char getSex() {
        return sex;
    }

    public void setSex(char sex) {
        this.sex = sex;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        if(age>0&&age<120){
            this.age = age;
        }else {//数据不合法
            this.age = 3;
        }

    }
}

继承

//所有的类都默认直接或者间接继承object类
public class Person /*extends Object*/{//父类、基类
    //修饰符:public protected default private
    private int money=10_0000_0000;
    public void say(){
        System.out.println("说了一句话");
    }

    public int getMoney() {
        return money;
    }

    public void setMoney(int money) {
        this.money = money;
    }
}
// student is a person
public class Student extends Person{//子类、派生类
    //快捷键  ctrl+H 可以看到继承关系
    //一个儿子只能有一个爸爸,一个爸爸可以有多个儿子
}

// teacher is a person
public class Teacher extends Person{//子类、派生类

}

 public static void main(String[] args) {
        Student student = new Student();
        student.say();//虽然student类中没有say方法以及mongey属性,但是仍然可以调用,就是因为继承了父类的public
        System.out.println(student.getMoney());
    }

super

public class Person {
    protected String  name="荔枝";

    public Person() {
        System.out.println("Person无参构造执行了");
    }

    public void print(){//如果修饰符换成private则无法继承,私有的不能继承
        System.out.println("Person");
    }
}

public class Student extends Person{
    private String name="ly";

    public Student() {
        //这里有一句隐藏代码 super(); 执行父类的无参构造
        super();// 必须在子类构造器的第一行,this()的使用也是这样
        System.out.println("Student无参构造执行了");
    }

    public void print(){
        System.out.println("Student");
    }
    public void test1(){
       print();
       this.print();
       super.print();
    }
    public void test(String name){
        System.out.println(name);//李颖
        System.out.println(this.name);//ly
        System.out.println(super.name);//荔枝
    }
}

 public static void main(String[] args) {
        Student student = new Student();//先调用了父类的无参构造,在调用的子类的无参构造
//        student.test("李颖");// 李颖 ly 荔枝
//        student.test1();//Student Student Person


    }

super注意点:
1.super调用父类的构造方法,必须在构造方法的第一个

​ 2.super 必须只能出现在子类的方法或者构造方法中!

​ 3.super和 this 不能同时调用构造方法!
Vs this:
​ 代表的对象不同:
​ this:本身调用者这个对象

​ super:代表父类对象的引用
​ 前提不同
​ this:没有继承也可以使用
​ super:只能在继承条件才可以使用

​ 构造方法不同
​ this():本类的构造

​ super():父类的构造

方法重写

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

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

    //静态方法与非静态方法的区别很大
    //静态方法:方法的调用只和左边,即定义的数据类型有关
    //非静态方法:重写
    public static void main(String[] args) {

        A a = new A();
        a.test();//A=>test()
        //父类的引用指向了子类
        B b = new A();
        b.test();//当两个类中的方法都为static时输出:B=>test(),当不是static而是进行了重写时输出:B=>test()
    }


重写:需要有继承关系的前提,子类重写父类的方法!
1.方法名必须相同
2.参数列表列表必须相同
3.修饰符:范围可以扩大但不能缩小:public>Protected>Default>private
4.抛出的异常:范围,可以被缩小,但不能扩大;classNotFoundException -->Exception(大)
重写,子类的方法名和参数和父类必须一致;方法体不同!
为什么需要重写?:父类的功能,子类不一定需要,或者不一定满足!快捷键:Alt + Insert :override

多态

动态编译类型,增强可扩展性

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

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

 public static void main(String[] args) {
        // 一个对象的实际类型是确定的
        // new Student();
        // new Person();

        //可以指向的引用类型就不确定,可以是父类的引用指向子类
        Student student = new Student();//Student 能调用的方法都是自己的或者继承父类的
        Person student1 = new Student();//Person 父类型,可以指向子类,但是不能调用子类独有的方法
        Object student2 = new Student();

        //对象执行了哪些方法,主要是与左边的类型有关,只有子类进行重写后,即使左边的父类,也会执行子类的方法
        student.run();
        student1.run();// 子类重写了父类的方法,执行子类的方法
        student.eat();
    }

多态注意事项:
1.多态是方法的多态,属性没有多态
2.需要存在父类和子类的联系 ,否则会出现类型转换异常:ClassCastException
3.存在条件:继承关系,方法需要重写,父类引用指向子类对象:Father f1 = new Son();
哪些不能进行重写?:1.static 方法,属于类,它不属于实例;2.final 常量 3.private方法

instanceOf关键字

用于判断两个类是否存在父子关系,true/false

 public static void main(String[] args) {
        //Object>String
        //Object>Person>Student
        //Object>Person>Teacher
//        System.out.println(X instanceof  Y);  能不能编译通过主要是看X和Y是否具有父子关系
        Object object = new Student();
        System.out.println(object instanceof Student);//true
        System.out.println(object instanceof Person);//true
        System.out.println(object instanceof Object);//true
        System.out.println(object instanceof Teacher);//false
        System.out.println(object instanceof String);//false
        System.out.println("================================");
        Person person = new Student();
        System.out.println(person instanceof Student);//true
        System.out.println(person instanceof Person);//true
        System.out.println(person instanceof Object);//true
        System.out.println(person instanceof Teacher);//false
//        System.out.println(person instanceof String);//编译不通过
        System.out.println("================================");
        Student student = new Student();
        System.out.println(student instanceof Student);//true
        System.out.println(student instanceof Person);//true
        System.out.println(student instanceof Object);//true
//        System.out.println(student instanceof Teacher);//编译不通过
//        System.out.println(student instanceof String);//编译不通过
        System.out.println("================================");
    }

类型转换

 public static void main(String[] args) {
        //类型的转换 :父与子
        //高                 低
        Person obj = new Student();
        //无法使用obj.go();调用student的go方法,因为obj是一个person类型的对象
        //需要进行强制转换才可以使用,由高到低
        //student将这个对象转换为Student类型,就可以使用Student类型的方法了
        ((Student)obj).go();

        Student student = new Student();
        student.go();
        Person person=student;
//        person.go();//编译错误,无法使用;子类转换为父类,可能会丢失自身本来的一些方法
    }

1.父类引用指向子类的对象

2.把子类转换为父类,向上转型;自动转换

3.把父类转换为子类,向下转型,需要强制转换;

4.作用:方便方法的调用,减少重复的代码,简洁

static补充

public class Student {
    public static int age;// 静态变量  多线程
    public double score;// 非静态变量

    public void run(){
        go();//非静态方法可以使用静态方法
    }
    public static void go(){
//        run();//静态方法不能使用非静态方法,静态方法是和类一起加载的,那时还没有非静态的方法加载
    }

    public static void main(String[] args) {

        go();
        new Student().run();

//        Student stu = new Student();
//        System.out.println(Student.age);
//        System.out.println(stu.age);
//        System.out.println(stu.score);
    }
}

public class Person {
    {
        //代码块(匿名代码块)
        System.out.println("匿名代码块");
    }
    static {
        //静态代码块(加载初始化的数据),永久只执行一次
        System.out.println("静态代码块");
    }

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

    public static void main(String[] args) {
        Person person = new Person();//先执行静态代码块,再执行匿名代码块,最后是构造方法
        Person person1 = new Person();//先执行匿名代码块,再是构造方法,这是没有静态代码块了,它只执行一次
    }
}

import  static java.lang.Math.random;
import  static java.lang.Math.PI;

public class Test {
    public static void main(String[] args) {
        System.out.println(random());//经过静态导入包后,就不用使用Math.random()生成随机数了,可以直接使用random
        System.out.println(PI);
    }
}

五、抽象类与接口

抽象类

//abstract 关键字定义抽象类
public abstract class Action {
    //这是一个约束方法,有人帮我们实现,就可以定义为抽象方法
    // abstract 关键字定义抽象方法 ,只有方法名字,没有具体的实现
    public abstract void doSomething();
}

//抽象类的所有方法,继承了它的子类,都必须实现它的方法,除非继承它的也是一个抽象类
//java的类只能单继承,但是接口可以多继承
public class A extends Action{
    @Override
    public void doSomething() {

    }
}

注意:

1.不能new这个抽象类,只能靠子类去实现它;

2.抽象类中可以写普通的方法

3.抽象方法必须在抽象类中
就是抽象的抽象,一种约束
思考题:

抽象类存在构造器么?——不存在

存在的意义?——抽象出来 提高开发效率

接口

//interface 定义的关键字,接口都需要有实现类
public interface UserService {
    //接口中定义的变量都是常量
    public static final int AGE=99;

    // 接口中的所有定义其实都是抽象的 public abstract  被省略
    public abstract void add(String name);
    void delete(String name);
    void update(String name);
    void query(String name);
}


public interface TimeService {
    void timer();
}

//implements 关键字:通过类实现接口
// 实现了接口的类,就需要重写接口中的所有方法
//利用接口实现多继承
public class UserServiceImpl implements UserService,TimeService{
    @Override
    public void timer() {

    }

    @Override
    public void add(String name) {

    }

    @Override
    public void delete(String name) {

    }

    @Override
    public void update(String name) {

    }

    @Override
    public void query(String name) {

    }
}

接口作用:

  1. 是一种约束

  2. 定义一些方法,让不同的人实现

  3. 接口中的方法都是public abstract 修饰的

  4. 接口中的变量都是public static final 修饰的

  5. 接口不能被实例化,因为接口中没有构造方法(抽象类也不能被实例化,只能继承)

  6. 通过implements关键字,可以实现多个接口

  7. 实现类必须要重写接口中的方法

六、内部类

内部类就是在一个类的内部再定义一个类,比如,A类中定义一个B类,那么B类相对于A类来说就是称为内部类,而A类相对B类就是外部类。

成员内部类

public class Outer {
    private int id = 10;
    public void out(){
        System.out.println("这是一个外部类的方法");
    }


    public class Inner {
        public void in(){
            System.out.println("这是一个内部类的方法");
        }
        //内部类可以获得外部类的私有属性或方法!!!
        public void getId(){
            System.out.println(id);
        }


    }
}

public class Application {
    public static void main(String[] args) {
        Outer outer = new Outer();
        //通过外部类来实例化内部类
        Outer.Inner inner = outer.new Inner();
        inner.in();
    }

}

静态内部类

成员内部类加上Static关键字就是静态内部类。

补充:

public class Outer {
}
//一个JAVA类中,可以有多个class类,但是只能有一个public class
class A{
    public static void main(String[] args) {
        
    }
}

局部内部类

public class Outer {
    //局部内部类
    public  void  method(){
        class Inner{
            public void in(){}
        }
    }
}

匿名内部类

public class Test {
    public static void main(String[] args) {
        //没有名字初始化类,不用将实例化保存到变量中
        new Apple().eat();
        new UserService(){

            @Override
            public void hello() {

            }
        };
    }
}
class Apple{
    public void eat(){
        System.out.println("apple");
    }
}
interface  UserService{
    void hello();
}

七、异常机制

异常体系结构

Error:

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

◆java虚拟机运行错误(Virtual MachineError),当JVM不再有继续执行操作所需的内存资源时,将出现 OutOfMemoryError。这些异常发生时,Java虚拟机(JVM)一般会选择线程终止:

◆还有发生在虚拟机试图执行应用时,如类定义错误(NoClassDefFoundError)、链接错误(LinkageError)。这些错误是不可查的,因为它们在应用程序的控制和处理能力之 外,而且绝大多数是程序运行时不允许出现的状况。

Exception:

◆在Exception分支中有一个重要的子类RuntimeException(运行时异常)

​ ◆ ArraylndexOutOfBoundsException(数组下标越界)
​ ◆NullPointerException(空指针异常)
​ ◆ ArithmeticException(算术异常)
​ ◆ MissingResourceException(丟失资源)
​ ◆ClassNotFoundException(找不到类)等异常,这些异常是不检查异常,程序中可以选择捕获处理,也可以不处理。
◆这些异常一般是由程序逻辑错误引起的,程序应该从逻辑角度尽可能避免这类异常的发生
◆Error和Exception的区别:Error通常是灾难性的致命的错误,是程序无法控制和处理的,当出现这些异常时,Java虚拟机(JVM)一般会选择终止线程;Exception通常情况下是可以被程序处理的,并且在程序中应该尽可能的去处理这些异常。

JAVA异常处理机制

异常处理的5个关键字: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){ //捕获异常,参数是想要捕获的异常类型
            System.out.println("程序出现异常!");
        }catch (Exception e){//可以捕获多个异常,从小到大的范围,只会对应输出一个
            System.out.println("exception 异常");
        }finally { //处理善后工作,可选,可以不要finally。假设在IO流中,可以用于关闭资源
            System.out.println("finally");
        }
    }


public class Test {
    public static void main(String[] args) {
        try {
            new Test().test(1,0);
        } catch (ArithmeticException e) {
            e.printStackTrace();
        } finally {
        }

    }
    //假设这个方法中,处理不了这个异常,那就在方法上抛出异常 throws
    public  void test(int a,int b) throws ArithmeticException {
        if(b==0){
            throw new ArithmeticException();//主动的抛出异常,一般在方法中使用
        }
        System.out.println(a/b);
    }
}

//快捷键:选中需要包裹内容, ctrl alt +T

自定义异常

◆使用Java内置的异常类可以描述在编程时出现的大部分异常情况。除此之外,用户还可以自定义异常。用户自定义异常类,只需继承Exception类即可。
◆在程序中使用自定义异常类,大体可分为以下几个步骤

1. 创建自定义异常类。
 2. 在方法中通过throw关键字抛出异常对象。
 3. 如果在当前抛出异常的方法中处理异常,可以使用try-catch语句捕获并处理;否则在方法的声明处通过throws关键字指明要抛出给方法调用者的异常,继续进行下一步操作。
 4. 在出现异常方法的调用者中捕获并处理异常。
//自定义的异常
public class MyException extends Exception{
    private int detail;

    public MyException( int detail) {
        this.detail = detail;
    }
    //toString 打印异常信息

    @Override
    public String toString() {
        return "MyException{" +
                "detail=" + detail +
                '}';
    }
}

public class Test {
    static void test(int a) throws MyException {
        System.out.println("传递的参数为:"+a);
        if(a>10){
            throw new MyException(a);
        }
        System.out.println("OK");
    }

    public static void main(String[] args) {
        try {
            test(20);
        } catch (MyException e) {
            System.out.println(e);;
        }
    }
}

经验总结:

◆处理运行时异常时,采用逻辑去合理规避同时辅助 try-catch 处理

◆在多重catch块后面,可以加一个catch(Exception)来处理可能会被遗漏的异常

◆对于不确定的代码,也可以加上 try-catch,处理潜在的异常尽量去处理异常,切忌只是简单地调用 printStackTrace()去打印输出,可以增加一些处理异常的代码

◆具体如何处理异常,要根据不同的业务需求和异常类型去决定

◆尽量添加finally语句块去释放占用的资源(比如IO流)