Java面向对象


Java面向对象


面向对象


什么是面向对象


  • 面向对象编程(Object-Oriented Programming,OOP)
  • 本质是:以类的方式组织代码,以对象的组织(封装)数据。
  • 三大特性:封装、继承、多态。

方法回顾


方法的调用:
静态类(标注static的类)

//主程序
public class Demo {
    public static void main(String[] args) {

        //直接调用
        Hello.say();

    }
}

//其他类方法Hello
public class Hello {
    public static void say(){
        System.out.println("Hello world!");
    }
}

非静态类:

//主程序
public class Demo {
    public static void main(String[] args) {

        //非静态类就要实例化这个类,new
        Hello hello = new Hello();
        hello.say();

    }
}

//其他类方法Hello
public class Hello {
    public void say(){
        System.out.println("Hello world!");
    }
}

其次还有,两个非静态类或者静态类可以相互调用:

public class Demo {
    public static void main(String[] args) {}
    
    //a可以调用b
    public void a(){
        b();
    }
    
    public void b(){}
}
public class Demo {
    public static void main(String[] args) {}

    //a可以调用b
    public static void a(){
        b();
    }

    public static void b(){}
}

原因是:static存在的很早,和类一起加载的。而非静态类是类实例化之后才存在的,已存在的调用不存在的会报错。

面向对象与面向过程

  • 面向对象:
    • 物以类聚,分类的思维模式,思考问题会首先解决问题需要哪些分类,然后对这些类进行单独思考。然后再是对某个类下的细节进行面向过程的思索。Java语言。
    • 适合处理复杂的问题,适合处理需要多人协作的问题。
  • 面向过程:
    • 步骤清晰,第一步、第二步等等。像C与C++,从上到下,处理问题。
    • 适合处理一些简单的问题。
  • 两者联系:相辅相成,对于复杂的事物,为了从宏观上把握、整体上分析,需要用面向对象的思路来分析整个系统,但是具体到微观,仍然需要面向过程的思路去分析。

类与对象


  • 类是一种抽象的数据类型,它是对某一事物整体描述/定义,但是并不能代表某一个具体的事物。
  • 对象是抽象概念的具体实例。

在一个程序中,只有一个主类,可以有很多的类,类里边有属性和方法,属性是抽象的,这些类通过主类进行调用。

//主类
public class Demo{
    public static void main(String[] args) {

        //类,是抽象的,要实例化成对象之后才可使用
        //通过new来实例化一个类,返回一个具体的对象,new可以new很多对象

        Student xiaoming = new Student();//从抽象的Student类中new出了一个具体的对象xiaoming
        Student xiaohong = new Student();//xiaohong

        //抽象的类是不会进行初始化的,不然就写死了,所以在对象调用属性的时候要对其进行初始化,否则就是默认的字符为null,数字为0.
        xiaoming.name = "小明";
        xiaoming.age = 3;

        System.out.println(xiaoming.name);//小明
        System.out.println(xiaoming.age);//3

        System.out.println(xiaohong.name);//null
        System.out.println(xiaohong.age);//0
    }
}

//Student类
public class Student {

    //属性,字段,
    String name;
    int age;

    //方法
    public void study(){
        System.out.println(this.name + "在学习");
    }
    //类里边只可能出现这两种东西
}

构造器


也叫构造方法,和类名相同,没有返回值,具体作用:

  1. 使用new关键字,本质是在调用构造器。
  2. 用来初始化值

一个类即使不写,它也会存在一个方法

//主类
public class Demo{
    public static void main(String[] args) {
        Student student = new Student();
    }
}

//Student
public class Student {
}
/*
这里还是可以new的,原因是因为自动在Student类里构造了一个构造器,一个空的
public class Student {
    public Student() {
    }
}
*/

显示的构造器:

定义了有参构造之后,如果想使用无参构造,是要显示定义无参的。

//主类
public class Demo{
    public static void main(String[] args) {
        Student student = new Student("xiaoming");

        System.out.println(student.name);//xiaoming
    }
}
//Student类
public class Student {

    String name;

    public Student(String name){
        this.name = name;
    }
}
/*
在这里,就显示化定义了一个构造器,如果显示化构造器了的话,如果传参了,那么在主类new对象的时候也要传参,下边如果还有初始化从方法体的话,在new完对象之后就初始化好了。
当然显示构造器和没有传参数的构造器是可以同时存在的,new对象的时候是可以选择的,根据类型来选择,也叫重载。
alt+insert可以快速构造,默认会根据你new对象的形式来构造,当然也可以select none
*/

内存分配图:

image-20211121165554988

封装


“高内聚,低耦合”,属性私有,get/set

意义:

  1. 提高程序的安全性,保护数据
  2. 隐藏代码的实现细节
  3. 统一接口
  4. 增加系统可维护性
//主类
public class Demo{
    public static void main(String[] args) {
        Student s1 = new Student();

        s1.setName("小明");

        System.out.println(s1.getName());//小明

    }
}

//Student类
public class Student {

    //属性私有
    private String name;

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


    public String getName(){
        return name;
    }

    public void setName(String name){
        this.name = name;
    }

}

//这个get/set可以自动生成,IDEA里用这个alt+insert

继承


  • 继承的本质是对某一批类的抽象,从而实现对现实世界更好的建模

  • extands的意思是“拓展”。子类是父类的拓展。

  • Java中类只有单继承,没有多继承

    1. 继承是类和类之间的一种关系,除此之外,类和类之间的关系还有依赖、组合、聚合等。
    2. 继承关系的两个类,一个为子类(派生类),一个为父类(基类)。子类继承父类,使用关键子extends来表示。
    3. 子类和父类之间,从意义上讲应该具有“is a”的关系。
  • 在Java类中,所有的类,都默认直接或者间接继承Object类

//主类
public class Demo{
    public static void main(String[] args) {
        Student student = new Student();
        student.say();//说了一句话

    }
}

//父类
public class Person {
    public void say(){
        System.out.println("说了一句话");
    }
}

//子类,继承了父类,就会有父类的所有方法
public class Student extends Person{

}
//ctrl + H 查看关系

Super


super注意点:

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

和this比较:

  1. 代表对象不同:

    • this:本身调用者这个对象
    • super:代表父类对象的应用
  2. 前提:

    • this:没有继承也可以使用
    • super:只能在继承条件下才可以使用
  3. 构造方法:

    • this():本类的构造
    • super():父类的构造

属性调用:

//主类
public class Demo{
    public static void main(String[] args) {

        Student student = new Student();
        student.test("小刚");//小刚 小明,小红

    }
}
//父类
public class Person {
    public String name = "小明";
}

//子类
public class Student extends Person{

    private String name = "小红";

    public void  test(String name){
        System.out.println(name);
        System.out.println(this.name);
        System.out.println(super.name);
    }
}

方法调用:

//主类
public class Demo{
    public static void main(String[] args) {

        Student student = new Student();
        student.test();//Student Student Person

    }
}
//父类
public class Person {
    public void print(){
        System.out.println("Person");
    }
}
//子类
public class Student extends Person{

    public void print(){
        System.out.println("Student");
    }

    public void  test(){
        print();
        this.print();
        super.print();
    }
}

关于构造器的一些super:

//主类
public class Demo{
    public static void main(String[] args) {

        Student student = new Student();
        //Person无参执行了
        //Student无参执行了
    }
}
//父类
public class Person {

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

}
//子类
public class Student extends Person{

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

}
/*
这里是因为,new Student对象的时候,在Student类的构造器第一行有个默认的super();借此来调用父类
如果父类的无参构造变成了有参,那么子类的super就会报错,这时候要么子类super传参或者在父类构造一个无参的构造器
*/

方法重写


重写:需要有继承关系,子类重写父类的方法

  1. 方法名必须相同
  2. 参数列表必须相同
  3. 修饰符:反围可以扩大但不能缩小: public
  4. 抛出的异常:范围,可以被缩小,但不能扩大。

重写的子类的方法和父类必要一致。

为什么要重写:父类的功能,子类不一定需要或者不一定满足。

静态类:

//主类
public class Demo{
    public static void main(String[] args) {

        Student a = new Student();
        a.print();//B==>

        Person b = new Student();
        b.print();//A==>
        
    }
}
//父类
public class Person {
    public static void print(){
        System.out.println("A==>");
    }
}
//子类
public class Student extends Person{

    public static void print(){
        System.out.println("B==>");
    }
}
//静态方法:方法的调用只和左边定义的有关

非静态类:

//主类
public class Demo{
    public static void main(String[] args) {

        Student a = new Student();
        a.print();//B==>

        Person b = new Student();//子类重写了父类的方法
        b.print();//B==>

    }
}
//父类
public class Person {
    public void print(){
        System.out.println("A==>");
    }
}
//子类
public class Student extends Person{

    public void print(){
        System.out.println("B==>");
    }
}
//有重写

多态


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

一个对象的实例类型是确定的,但可以指向对象的引用的类型有很多(父类,有关系的类)

  • 使用父类类型的引用指向子类的对象,该引用只能调用父类中定义的方法和变量
  • 如果子类重载了父类的方法,只会使用子类的方法。
  • 子类调用的方法都是自己的或者从父类中继承的
  • 可以强制转换,想要调用子类独有的,在用父类引用的时候,可以强制转换成子类,例如如果子类有个ect();方法,在如下的程序中想要调用的话,可以变为((Student) s2).ect();

注意事项:

  1. 多态是方法的多态,属性没有多态
  2. 子类和父类,有联系。否则类型转换异常,ClassCastException!
  3. 存在条件:继承关系,方法需要重写,父类引用指向子类对象。 Father f1 = new Son();
    • 无法重写的:
      1. static 方法,属于类,不属于实例
      2. final 常量;
      3. private方法;
//主类
public class Demo{
    public static void main(String[] args) {
        Student s1 = new Student();
        Person s2 = new Student();

        s1.print();//B==>,子类重写了父类的方法,执行了子类的方法
        s2.print();//B==>
    }
}
//父类
public class Person {
    public void print(){
        System.out.println("A==>");
    }
}
//子类
public class Student extends Person {
    @Override
    public void print() {
        System.out.println("B==>");

    }
}

instanceof


对于System.out.println(X instanceof Y);

  1. 能否编译通过看X与Y是否存在父子关系
  2. T/F看引用指定对象,x指向的对象如果是后边Y的子类,则是T
  3. 如果X引用指向的对象与Y是父子关系,Y即继承前者,那么编译是false。

Static关键字


Static代码块:最先加载,只执行一次

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

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

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

    public static void main(String[] args) {
        Demo s1 = new Demo();//静态代码块 匿名代码块 构造方法
        Demo s2 = new Demo();//匿名代码块 构造方法

    }
}

导入别的类的静态方法:

import static java.lang.Math.random;

public class Demo{
    
    public static void main(String[] args) {
        System.out.println(random());
    }
}

抽象类


加上abstract标签的就是抽象,有抽象方法和抽象类。

特点:

  1. 不能new这个抽象类,只能靠子类去实现他,约束。
  2. 抽象类中可以写普通方法
  3. 抽象方法必须在抽象类中

存在的意义:提高开发效率

//父类
public abstract class Person {
    //抽象方法,只有方法名字,没有方法的实现
    public abstract void doSomething();
}
//子类
//抽象类的所有方法,继承了它的子类,都必须要实现它的方法(重写),除非它自己也是抽象,那就由子子类实现
public class Student extends Person {
    @Override
    public void doSomething() {

    }
}

接口的定义与实现


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

接口就是一组规则。接口的本质是契约。

OO的精髓,是对对象的抽象,最能体现这一点的就算接口。因为设计模式所研究的,实际上就算如何合理的去抽象。

声明类的关键字为class,声明接口的关键字是interface

作用:

  1. 约束
  2. 定义一些方法,让不同的人实现
  3. public abstract
  4. public static final
  5. 接口不能被实例化,接口没有构造方法
  6. implements可以实现多个接口
  7. 必须要重写接口中的方法
//接口函数
//interface定义的关键字
public interface UserService {
    void add(String name);
    void delete(String name);
    void update(String name);
}
//实现方法,可以继承多个
public class UserServicelmol implements UserService{
    @Override
    public void add(String name) {

    }

    @Override
    public void delete(String name) {

    }

    @Override
    public void update(String name) {

    }
}

内部类


(不推荐)

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

  • 成员内部类
  • 静态内部类
  • 局部内布类
  • 匿名内部类
  • lambaba

成员内部类:

//主类
public class Demo{
    public static void main(String[] args) {
        Outer outer = new Outer();
        //通过这个外部类来实例化内部类
        Outer.Inner inner = outer.new Inner();
        inner.in();//这是一个内部类
        inner.getID();//10

    }
}
//子类
public class Outer {

    private int id;
    public void out(){
        System.out.println("这是一个外部类");
    }

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

静态内部类:

public class Outer {

    private int id;
    public static void out(){
        System.out.println("这是一个外部类");
    }

    public static class Inner{
        public void in(){
            System.out.println("这是一个内部类");
        }
    }
}

一个Java类里可以有很多class,但只能有一个public class

局部内部类:

//类似这样子的。但这个我写的是极其不规范的,这只是给举例
public class Demo{
   //局部内部类
    public void method(){
        
        class Inner{
            public void in(){
                
            }
        }
    }
}

匿名初始化类/接口“

public class Demo{
    public static void main(String[] args) {
        //匿名初始化类,不用把实例保存在实例变量中
        new Apple().eat();

        new UserServide() {
            @Override
            public void hello() {

            }
        };
    }
}
class Apple{
    public void eat(){

    }
}
interface UserServide{
    void hello();
}

异常机制


Exception

异常指程序中出现的不期而至的各种状况,如:文件找不到、网络连接失败、非法参数等。

异常发生在程序运行期间,它影响了正常的程序执行流程。

主要分为三类:

  1. 最具代表的是用户错误或问题引起的异常,例如要打开一个不存在的文件,这些异常在编译的时候不能被简单的忽略。
  2. 运行时异常,运行时异常可能是被程序员避免的异常。可以在编译时候被忽略。
  3. 错误,错误不是异常,通常在代码中被忽略,例如栈溢出,一个错误就发生了,它们在编译的时候也检查不到。

Java把异常当作对象来处理,并定义一个基类java.lang.Throwable作为所有异常的超类。

在Java API中已经定义了很多异常类,这些异常类被分为两大类,错误Error和Exception。

Error:

  • Error类对象由Java虚拟机生成并抛出,大多数错误与代码编写者所执行的操作无关。
  • Java虚拟机运行错误(Virtual MachineError),当JVM不再有继续执行操作所需的内存资源时,出现OutOfMemoryError。这些异常发生时,Java虚拟机(JVM)一般会选择线程终止;
  • 还有发生在虚拟机试图执行应用时,如类定义错误(NoClassDefFoundError)、链接错误(LinkageError)。这些错误不可查。

Exception

  • 在Exception分支中有一个重要的子类RuntimeException(运行时异常)
    • ArrayIndexOutOfBoundsException(数组下标越界)
    • NullPointerException(空指针异常)
    • ArithmeticExpection(算术异常)
    • MissingResourceException(丢失资源)
    • ClassNotFoundException(找不到类)
    • 这些异常是不检查异常,程序中可以选择捕获处理,也可以不处理。
  • 这些异常一般是由程序逻辑错误引起的。

捕获和抛出异常


try、catch、finally关键字,好处是可以捕获异常,可以让程序执行下去

public class Demo{
    public static void main(String[] args) {
        int a =1;
        int b =0;

        try {//try 监控区域
            System.out.println(a / b);
        }catch (ArithmeticException e){//catch(想要捕获的异常,最高级别为Throwable) 捕获异常
         catch(Throwable e){//最大的写在后边
             
         }
            System.out.println("程序出现异常,变量b不能为零");
        }finally {//善后
            System.out.println("finally");
        }
        //finally 可以不要 假设IO,资源,关闭
    }
}
/*
程序出现异常,变量b不能为零
finally
*/
//快捷键ctrl+alt+t+6/7/8

throw关键字:

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

        new Demo().est(1,0);

    }
    public void est(int a,int b){
        if(b == 0){
            throw new ArithmeticException();//主动抛出异常,一般在方法中使用
        }
        System.out.println(a / b);
    }
}
/*
Exception in thread "main" java.lang.ArithmeticException
	at Demo.est(Demo.java:9)
	at Demo.main(Demo.java:4)
*/

throws关键字:

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

        new Demo().est(1,0);

    }
    public void est(int a,int b) throws  ArithmeticException{
        System.out.println(a / b);
    }
}
/*
Exception in thread "main" java.lang.ArithmeticException: / by zero
	at Demo.est(Demo.java:8)
	at Demo.main(Demo.java:4)
*/

自定义异常


(用的不多)

在程序中自定义异常,只需要继承Exception类即可,可以分为以下几个步骤:

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

    public MyException(int a){
        this.datail = a;
    }

    @Override
    public String toString() {
        return "MyException{" + "datail=" + datail +'}';
 
   }
}
//测试类1
public class Demo{
    public static void main(String[] args) {
        try {
            test(10);
        } catch (MyException e) {
            e.printStackTrace();
        }

    }
    static  void test(int a) throws MyException {
        System.out.println("传递的参数为:" + a);
        if(a>10){
            throw new MyException(a);
        }
        System.out.println("OK");
    }
}
/*
传递的参数为:10
OK
*/
//测试类2
public class Demo{
    public static void main(String[] args) {
        try {
            test(11);
        } catch (MyException e) {
            e.printStackTrace();
        }

    }
    static  void test(int a) throws MyException {
        System.out.println("传递的参数为:" + a);
        if(a>10){
            throw new MyException(a);
        }
        System.out.println("OK");
    }
}
/*
MyException{datail=11}
传递的参数为:11
	at Demo.test(Demo.java:13)
	at Demo.main(Demo.java:4)
*/

实际应用中的经验总结


  • 处理运行时异常时,采用逻辑去合理规避同时辅助try-catch处理。
  • 在多重catch块后面,可以加一个catch(Exception)来处理可能会被遗漏的异常。
  • 对于不确定的代码,也可以加上try-catch,处理潜在的异常。(摁住ALT+ENTER键)
  • 尽量去处理异常,切忌只是简单的调用printStrackTrace()去打印输出。
  • 具体如何处理异常,要根据不同的业务需求和异常类型去决定。
  • 尽量添加finally语句去释放占用的资源。

相关