Java面向对象

类和对象

面向对象

  • 面向:拿、找
  • 对象:相应工具
  • 面向对象编程:拿相应工具过来做对应的事情

类(设计图):是对象共同特征的描述;

对象:是真实存在的具体事物。

在Java中,必须先设计类,才能获得对象。

类的定义格式:

public class 类名{

        1、成员变量(代表属性,一般是名词)

        2、成员方法(代表行为,一般是动词)

        3、构造方法

        4、代码块

        5、内部类

}

例:

public class Phone{

        //属性(成员变量)

        String brand;

        double price;

        //行为(成员方法)

        public void call(){

        }

        public void playGame(){

        }

}

类的对象创建格式

类名 对象名 = new 类名();

例:

Phone p = new Phone();

对象的使用格式:

  • 访问属性:对象名.成员变量
  • 访问行为:对象名.方法名(...)
  • 用来描述一类事物的类,专业名词为:Javabean类

在Javabean类中,是不写main方法的。

  • 在以前,编写main方法的类,叫做测试类

我们可以在测试类中创建javabean类的对象并进行赋值调用。

  • 成员变量的完整定义格式是:修饰符 数据类型 变量名称 = 初始化值;一般无需指定初始化值,存在默认值。

权限修饰符

权限修饰符是用来控制一个成员能够被访问的范围的。可以修饰成员变量,方法,构造方法,内部类。

有四种作用范围由小到大(private<空着不写(缺省/默认)<protected<public

修饰符 同一个类中

同一个包中

其他类

不同包下的子类 不同包下的其他类
private
空着不写
protected
public

权限修饰符的使用规则

实际开发中,一般值用private和public

  • 成员变量私有
  • 方法公开

特例:一个方法被其他多个方法调用,那么这个方法一般也私有。

private关键字

  • private是一个权限修饰符。
  • 可以修饰成员(成员变量和成员方法)。
  • 被private修饰的成员只能在本类中才能访问。
  • 针对private修饰的成员变量,如果需要被其他类使用,需要提供get和set方法。
  • set方法:给成员变量赋值。
  • get方法:对外提供成员变量的值。
  • set和get方法需要用public修饰,若使用private修饰则无法在其他类中使用该方法。

this关键字

成员变量和局部变量

  • 定义在方法中的变量,称为局部变量
  • 定义在方法外类的里面的变量,称为成员变量
区别 成员变量 局部变量
类中位置不同 类中,方法外 方法内,方法申明上
初始化值不同 有默认初始化值 没有,使用之前需要赋值
内存位置不同 堆内存 栈内存
生命周期不同 随着对象的创建而存在,随着对象的消失而消失 随着方法的调用而存在,随着方法的运行结束而消失
作用域 整个类中有效 当前方法中有效
public class people{
    private int age;//成员变量
    public void method(){
        int age = 10;//局部变量
        System.out.println(age);
    }
}

Java中代码执行过程中调用变量将遵守就近原则,即执行某语句时,该语句中含有变量,若存在同名的变量,则该语句会调用离他最近的变量。如上面代码所示,输出语句中的age,所带入的时局部变量的age。

this关键字的作用:可以区分成员变量和局部变量,可避免就近原则

public class people{
    private int age;//成员变量
    public void method(){
        int age = 10;//局部变量
        System.out.println(this.age);
    }
}

如上代码,此时,输出语句中的age便是调用成员变量的age。

this的本质:代表方法调用者的地址值

super关键字

super:代表父类的存储空间。

static关键字

static表示静态,是Java中的一个修饰符,可以修饰成员方法,成员变量

静态变量

被static修饰的成员变量,叫作静态变量

特点:

  • 被该类所有对象共享(即某个对象对本身的静态变量赋值后,该类的所有其他对象具有这个值)
  • 不属于对象,属于类
  • 静态变量跟对象无关,随类的加载而加载的,优先于对象出现的

调用方式:

  • 类名调用(推荐)
  • 对象名调用

静态变量存储在静态储存位置(静态区位于堆内存中)。

静态方法

被static修饰的成员方法,叫做静态方法

特点:

  • 多用在测试类和工具类中
  • Javabean类中很少会用

调用方式:

  • 类名调用(推荐)
  • 对象名调用

static的注意事项

  • 静态方法只能访问静态变量和静态方法
  • 非静态方法可以访问静态变量或者静态方法,也可以访问非静态的成员变量和非静态的成员方法。但是,特殊情况下,静态方法也是能够去调用非静态变量和非静态方法的,例如常见的主方法public static void main(String[] args)”,主方法是静态的,但是却会调用各种非静态变量或非方法,这是因为在调用非静态变量或方法之前有创建相关的对象,有了对象之后便能调用非静态变量和非静态方法了。故,若在静态方法中调用非静态变量与非静态方法需要事先创建对象。
  • 静态方法中没有this关键字

final关键字

final关键字可以修饰方法,类及变量,情况如下

final修饰方法:

        表明该方法是最终方法,不能被重写。

final修饰类:

        表明该类是最终类,不能被继承。

final修饰变量:

        表明该变量是常量,只能赋值一次,并且定义时必须赋值。

常量

实际开发中,常量一般作为系统的配置信息,方便维护,提高可读性。

常量的命名规范:

  • 单个单词:全部大写
  • 多个单词:全部大写,单词之间用下划线隔开

细节:

final修饰的变量是基本类型:那么变量存储的数据值不能发生改变。

final修饰的变量是引用类型:那么变量存储的地址值不能发生改变,对象内部所存储的信息可以改变。

构造方法

构造方法也叫作构造器、构造函数。

作用:在创建对象的时候给成员变量进行赋值的。

构造方法的格式:

public class Student{

        修饰符  类名(参数){

                方法体;

        }

}

注意点:

  1. 方法名与类名相同,大小写也要一致。
  2. 没有返回值类型,包括void也没有。
  3. 没有具体的返回值(不能由return带回结果数据)。

执行时机:

  1. 创建对象的时候由虚拟机调用,不能手动调用构造方法。
  2. 每创建一次对象,就会调用一次构造方法。

例:

public class Person{

        private String name;

        private int age;

        public Person(){

                ......

        }//空参构造

        public Person(String name,int age){

                ......

        }//带参构造

}

注意事项:

  • 如果在类中,没有定义任何构造方法,系统将给出一个默认无参数构造方法
  • 如果定义了构造方法,系统将不再提供默认的构造方法。
  • 系统重载:
    • 带参构造方法,和无参数构造方法,两者方法名相同,但是参数不同,这叫做构造方法的重载

推荐使用方式:无论是否使用,都手动书写无参数构造方法,和带全部参数的构造方法。

其他

标准的Javabean类

用来描述一类事物的类

①类名需要见名知意

②成员变量使用private修饰

③提供至少两个构造方法

  • 无参数构造方法
  • 带全部参数的构造方法

④成员方法

  • 提供每一个成员变量对应的set***()/get***()
  • 如果还有其他行为,也需要写上

测试类

用来检查其他类是否书写正确,带有main方法的类。

工具类

不是用来描述一类事物的,而是帮我们做一些事情的类。

①类名需要见名知意

②私有化构造方法

目的:为了不让外界创建他的对象。其对象无实际意义

③方法定义为静态

方便调用,通过类名进行调用。

构造代码块(仅了解)

写在成员位置的代码块。

作用:可以把多个构造方法中重复的代码抽取出来。

执行时机:我们在创建本类对象的时候会先执行构造代码块在执行构造方法。

静态代码块

格式:static{}

特点:需要通过static关键字修饰,随着类的加载而加载,并且自动触发,只执行一次

使用场景:在类加载的时候,做一些数据初始化的时候去使用

继承

封装:对象代表什么,就得封装相应的数据,并提供数据对应的行为。

继承:继承是面向对象三大特征之一,可以让类跟类之间产生父子的关系。

继承格式:

public class 子类 extends 父类{}

Java中提供一个关键字extends,用这个关键字,我们可以让一个和另一类建立起继承关系。

public  class  Student  extends  Person{}

Student称为子类(派生类),Person称为父类(基类或超类)

使用继承的好处:

  • 可以把多个子类重复的代码抽取到父类中,提高代码的复用性。
  • 子类可以在父类的基础上,增加其他的功能,使子类更强大。

继承的特点

Java只支持单继承,不支持多继承,但支持多层继承。单继承:一个子类只能继承一个父类。不吃成多继承:子类不能同时继承多个父类。多层继承:子类A继承父类B,父类B可以继承父类C,其中B为A的直接父类,C为A的间接父类。每一个类都直接或者间接地继承于Object,即如果一个类没有继承任何类,则JVM会默认其继承Object。

子类只能访问父类中非私有的成员。

子类能从父类中继承的内容:

1、构造方法均不能继承

父类的构造方法不能被子类继承,因为父类的构造方法的方法名与子类的类名不同,故不能继承。

2、成员变量都能继承

成员变量无论是否有Private修饰,该变量都能继承;但是,被Private修饰的变量无法进行调用。

3、成员方法,非私有的可以继承,私有的不能继承

只有父类中的虚方法(非private、非static、非final修饰的方法为虚方法)才能被子类继承;父类会将自身的虚方法建成一张虚方法表,并把虚方法表交给子类。

访问特点

成员变量的访问特点

继承中成员变量访问特点:就近原则

先在局部位置找,本类成员位置找,父类成员位置找,逐级往上。

重名变量情况

System.out.println(name);//从局部位置开始往上找

System.out.println(this.name);//从本类成员位置开始往上找

System.out.println(super.name);//从父类成员位置开始往上找

构造方法的访问特点

父类中的构造方法不会被子类继承。

子类中所有的构造方法默认先访问父类中的无参构造,再执行自己。原因是:(1)子类在初始化的时候,有可能会使用到父类中的数据,如果父类没有完成初始化,子类将无法使用父类的数据;(2)子类初始化之前,一定调用父类构造方法先完成父类数据空间的初始化。子类构造方法的第一行语句默认都是:super(),不写也存在即JVM默认会有,且必在第一行;若想调用父类有参构造,必须手动写super进行调用。

方法的重写

当父类的方法不能满足子类现在的需求时,需要进行方法重写。

书写格式:

在继承体系中,子类出现了和父类一模一样的方法声明,我们就称子类这个方法时重写的方法。

@Override重写注解

  1. @Override写在重写后的方法上,校验子类重写时语法是否正确。
  2. 加上注解后如果有红色波浪线,表示语法错误。
  3. 建议重写方法都加@Overtide注解,同时可读性好。

重写的本质

方法重写的本质是:子类在继承父类的虚方法表示,若出现了方法重写的情况,则该子类的虚方法表会将自己重写的方法覆盖继承下来的被重写的方法。

方法重写的注意事项和要求:

  1. 重写方法的名称、形参列表必须与父类中的一致。
  2. 子类重写父类方法时,访问权限子类必须大于等于父类。
  3. 子类重写父类方法时,放回值子类必须小于等于父类
  4. 建议:重写的方法尽量和父类一样。
  5. 只有被添加到虚方法表中的方法才能被重写。

多态

多态:同类型的对象,表现出的不同形态。

多态的表达方式:

父类类型  对象名称  =  new  子类类型();

例.

父类与子类的代码:

public class Animals {
    String name;
    int number;

    public Animals(){

    }

    public Animals(String name,int number){
        this.name = name;
        this.number = number;
    }

    public void eat(){
        System.out.println("吃饭");
    }
}
public class Cat extends Animals{
    @Override
    public void eat() {
        System.out.println("吃鱼");
    }
}
public class Dog extends Animals{
    @Override
    public void eat() {
        System.out.println("吃骨头");
    }
}

主函数代码:

public class test {
    public static void main(String[] args){
        Animals animals = new Animals();
        Animals dog = new Dog();
        Animals cat = new Cat();

        as(animals);
        as(dog);
        as(cat);
    }

    public static void as(Animals a){
        a.eat();
    }
}

结果如下:

Java面向对象_第1张图片

多态的前提

  • 有继承关系
  • 有父类引用指向子类对象
  • 有方法重写

多态调用成员的特点

  • 调用成员变量:编译看左边,运行也看左边
    • 编译看左边的意思:javac编译代码时,会先看左边的父类中有没有这个变量,如果有,编译成功,若没有则编译失败。
    • 运行也看左边的意思:java运行代码的时候,实际获取的就是左边父类中的成员变量的值。
  • 调用成员方法:编译看左边,运行看右边
    • 编译看左边的意思:javac编译代码的时候,会看左边的父类中有没有这个方法,如果有,编译成功,若没有则编译失败,出现报错。
    • 运行看右边的意思:Java运行代码的时候,实际上运行的是子类中的方法。

多态的优势

  • 在多态形式下,右边对象可以实现解耦合,便于扩展和维护。

Person p = new Student();

p.work();//业务逻辑发生变化时,后续代码无需修改

即当不需要学生对象而是老师对象时,可以通过只修改此处的构造方法,而之后的代码都不需要修改。

  • 定义方法的时候,使用父类型作为形参,可以接受所有子类对象,体现多态的扩展性与便利。

多态的弊端

不能调用子类的特有功能,即调用方法时,编译看左边,运行看右边,那么在编译的时候会先检查左边的父类中有没有这个方法,如果没有则会直接报错。

解决方案:通过强制转换将对象由父类类型变为子类类型

父类类型  对象名  =  new  子类类型();//多态创建对象

子类类型  对象名  =  (子类类型)  对象名;//将该对象强制转换为子类类型。

tips:强制转换时不能随意转换,转换后的子类类型应与new时的子类类型相同才能转换成功,若转换的为其他子类类型,则会出现报错。

instanceof关键字可以用于判断创建的对象是哪种创建的子类类型,用法为:对象名  instanceof  子类类型;

“对象名  instanceof  子类类型  转换后的对象名”,这种用法为JDK14所新加的特性,可以先判断该对象是否为该子类类型,如果是,则强转成功,转换之后变量名改变,如果不是,则强转失败,结果为false。

包就是文件夹,用来管理各种不同功能的Java类,方便后期代码维护。

  • 包名的规则公司域名反写+包的作用,需要全部英文小写,见名知意。

全类名(全限定名):包名+类名,用import关键字导入包时用到,特殊情况下需要用其创建对象。

使用其他类的规则

  • 使用同一个包中的类时,不需要导包。
  • 使用java.lang包中的类时,不需要导包。因为该包为Java的核心包。
  • 其他情况都需要导包。
  • 如果同时使用两个包中的同名类时,需要用全类名,即创建两个同类名的不同对象时,需要用全类名进行创建。

抽象类和抽象方法

抽象方法:共性的(即相同的)行为(方法)抽取到父类之后。由于每一个子类执行的内容是不一样的,所以,在父类中不能确定具体的方法体。该方法就可以定义为抽象方法。

抽象类:如果一个类中存在抽象方法,那么该类就必须声明为抽象类。

抽象方法的定义格式:

public abstract 返回值类型 方法名(参数列表);

抽象类的定义格式:

public abstract class 类名{}

抽象类和抽象方法的注意事项

抽象类不能实例化,即抽象类不能创建对象。

抽象类中不一定有抽象方法,有抽象方法的类一定是抽象类。

可以有构造方法,当创建子类对象的时候,给属性进行赋值。

抽象类的子类,要么重写抽象类中的所有抽象方法,要么是抽象类(这种情况一般不可取)。

抽象类和抽象方法的意义

防止子类中重写同类行为(同名方法)的结构过于多样化,通过抽象类可以强制子类必须按照某种格式进行重写。

接口

接口的作用是通过接口可以实现不相关类的相同行为,而不需要了解对象所对应的类。即接口更一种规则,是对行为的抽象。

接口的定义和使用

接口用关键字interface来定义

public interface 接口名{}

接口不能实例化

接口和类之间是实现关系,通过implements关键字表示

public class 类名 implements 接口名{}

接口的子类(实现类)要么重写接口中的所有抽象方法,要么是抽象类。

接口和类的实现关系,可以单实现,也可以多实现。

public class 类名 implements 接口名1,接口名2{}

实现类还可以在继承一个类的同时实现多个接口。

public class 类名 extends 父类 implements 接口名1,接口名2{}

接口中成员的特点

  • 成员变量
    • 只能是常量
    • 默认修饰符:public static final
      • final表明其为常量,不能被修改
      • static能保证可以通过“接口名.常量名”这种方法进行调用,即方便调用
  • 构造方法
    • 没有构造方法,因为接口不需要创建对象,并且其不需要给子类的成员变量赋值。
  • 成员方法
    • 只能是抽象方法
    • 默认修饰符:public abstract
    • JDK7即之前:接口中只能定义抽象方法。

接口和类之间的关系

  • 类和接口的关系
    • 实现关系,可以单实现,也可以多实现,还可以在继承一个类的同时实现多个接口
  • 接口和接口的关系
    • 继承关系,可以单继承,也可以多继承。如果继承的是子接口,则需要重写他所继承的所有方法及其自身的方法。

JDK8以后接口中新增的方法

默认方法

允许在接口中定义默认方法,需要使用关键字default修饰,作用:解决接口升级的问题。

接口中默认方法的定义格式:

格式:public default 放回值类型 方法名(参数列表){        }

范例:public default void show(){        }

接口中默认方法的注意事项:

默认方法不是抽象方法,所以不强制重写。但是如果被重写,重写的时候需要去掉default关键字。

public是默认修饰符,可以省略,但default不能省略。若省略,则会被识别为是一个抽象方法。

如果实现了多个接口,多个接口中存在相同名字的默认方法,子类就必须对该方法进行重写。

静态方法

允许在接口中定义静态方法,需要用static修饰。

接口中静态方法的定义格式:

格式:public static 放回值类型 方法名(参数列表){        }

范例:public static void show() {        }

接口中静态方法的注意事项:

静态方法只能通过接口名调用,不能通过实现类名或者对象名调用。

public可以省略,static不能省略。若省略,则会被识别为是一个抽象方法。

JDK9接增的方法

接口中私有方法的定义格式:

格式1:private 放回值类型 方法名(参数列表){        }

范例1:private void show(){        }

//该方法一般由默认方法调用

格式2:private static 返回值类型 方法名(参数列表){        }

范例2:private static void show(){        }

//该方法一般由静态方法调用

私有方法的意义在于防止接口外的类调用。

接口的应用

1、接口代表规则,是行为的抽象。想让哪个类拥有一个行为,就让这个类实现对应的接口就可以了。

2、当一个方法的参数是接口时,可以传递接口所有实现类的对象,这种方式称之为接口多态。

适配器设计模式

设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性、程序的重用性。即设计模式就是各种套路。

适配器设计模式:解决接口与接口实现类之间的矛盾问题。

当一个接口中抽象方法过多,但是我只要使用其中一部分的时候,就可以使用适配器设计模式

实现方式:

编写中间类xxxAdapter,实现对应的接口

对接口中的抽象方法进行空实现,即重写方法体为空

让真正的实现类继承中间类,并重写需要用的方法

为了避免其他类创建适配器类的对象,中间的适配器类用abstract进行修饰

内部类

写在一个类里面的类就叫做内部类。

在一个类A的里面,再定义一个类B,类B就是内部类,类A是外部类,除A、B两个类的类称为外部其他类。

内部类表示的事物是外部类的一部分,并且内部类单独出现没有任何意义

内部类的访问特点

内部类可以直接访问外部类的成员,包括私有。

外部类要访问内部类的成员,必须创建对象。

成员内部类

写在成员位置的,属于外部类的成员。

成员内部类可以被一些修饰符所修饰,比如:private,默认,protected,public,static等。

在成员内部类里面,JDK16之前不能定义静态变量,JDK16开始才能定义静态变量。

获取成员内部类对象

方式一:

在外部类中编写方法,对外提供内部类的对象

范例:public class Outer{

                String name;

                

                class Inner{        }

                public Inner getInstance(){

                        return new Inner()

                   }

           }

之后通过创建一个Outer类的对象,然后用该对象调用方法即可获得内部类的对象。

方式二:

直接创建格式:外部类名.内部类名 对象名 = 外部类对象.内部类对象;

范例:Outer.Inner oi = new Outer().new Inner();

成员内部类如何获取外部类的成员变量

格式:外部类名.this.成员变量名

范例:

public class Outer{
    private int a = 10;
    
    class Inner{
        private int a = 20;
        
        public void show(){
            int a =30;
            System.out.println(a);//30
            System.out.println(this.a);//20
            System.out.println(Outer.this.a);//30
        }
    }
}//Outer.this获取了外部类(Outer)的地址值

静态内部类

静态内部类也是成员内部类中的一种。

静态内部类只能访问外部类中的静态变量和静态方法,如果想要访问非静态的需要创建外部类的对象。

创建静态内部类对象的格式:外部类名.内部类名 对象名 = new 外部类名.内部类名();

范例:Outer.Inner oi = new Outer.Inner();

由于有static关键字修饰,故不需要外部类对象,只需要外部类类名即可。

调用非静态方法的格式:先创建对象,用对象调用

调用静态方法的格式:外部类名.内部类名.方法名();

局部内部类

将内部类定义在方法里面就叫做局部内部类,类似于方法里面的局部变量。

外界是无法直接使用局部内部类,需要在方法内部创建对象并使用。

该类可以直接访问外部类的成员,也可以访问方法内的局部变量。

匿名内部类

匿名内部类就是隐藏了名字的内部类,可以写在成员位置,也可以现在局部位置。

格式:

new 类名或接口名(){

          重写方法;

}

其中实际上包含了继承或实现、方法重写、创建对象。

整体就是一个类的子类对象或者接口的实现类对象。

使用场景

当方法的参数是接口或者类时,以接口为例,可以传递这个接口的实现类对象,如果实现类只要使用一次,就可以用匿名内部类简化代码,不用再定义一个新的实现类了。

你可能感兴趣的:(java,开发语言)