JAVA学习之面向对象

面向对象

创建对象的过程

  1. 在第一次遇到一个类的时候,对这个类要进行加载,只加载一次。
  2. 创建对象,在堆中开辟空间
  3. 对对象进行初始化操作,属性赋值都是默认的初始值
  4. new关键字调用构造器,执行构造方法,在构造器中对属性进行重新赋值

this 的使用

this指代的是当前的对象
在这里插入图片描述
this关键字用法

  1. this可以修饰属性: 注意就近原则
public class Person {
    private String name;
    private int age;
    private double height;

    // this 在构造器中的使用
    public Person(String name, int age,double height) {
        this.name = name;
        this.age = age;
        this.height = height;
    }


    public Person(){

    }


    public void eat(){
        int age = 10;
        System.out.println(age);// 就近原则 age指的是局部变量的age,而不是成员变量的age
        System.out.println(this.age); // this.age 指代类的属性age
        System.out.println("I like eat");
    }

    public static void main(String[] args) {
        Person p = new Person("lili",12,160.5);
        p.eat();
    }
}
  1. this 修饰方法
public void eat(){
        int age = 10;
        System.out.println(age);// 就近原则 age指的是局部变量的age,而不是成员变量的age
        System.out.println(this.age); // this.age 指代类的属性age
        System.out.println("I like eat");
    }
    
    public void play(){
        this.eat();
        System.out.println("玩");
    }
  1. this 修饰构造器
public class Person {
    private String name;
    private int age;
    private double height;

    // this 在构造器中的使用
    public Person(String name, int age,double height) {
        this(name,age); // this修饰构造器必须放在第一行
        this.height = height;
    }

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

    public Person(){

    }
}

static 的使用

static可以修饰:属性,方法,代码块,内部类

static 修饰属性

public class Test {
    int id;
    static int sid;

    public static void main(String[] args) {
        Test t1 = new Test();
        t1.id = 1;
        t1.sid = 10;
        Test t2 = new Test();
        t2.id = 2;
        t2.sid = 20;
        Test t3 = new Test();
        t3.id = 3;
        t3.sid = 30;
        
    }
}

内存分析
在这里插入图片描述
一般官方的推荐static修饰属性的访问方式

// 通过类名.属性名的方式进行访问
Test.sid = 100;

static修饰属性总结:

  1. 在类加载的时候一起载入方法区中的静态域中
  2. 先于对象进行创建
    static修饰属性的应用场景:
  3. 某些特定的数据想要在内存中共享,只有一块 --> 这种情况下就可以用static修饰的属性

属性:
静态属性(类变量)
实例属性(静态变量)

static修饰方法

public class Test {
    int id;
    static int sid;

    public void a() {
        System.out.println(id);
        System.out.println(sid);

    }

    // static 和 public 都是修饰符,并列的没有先后顺序
    static public void b(){
        //a(); 在静态方法中不能访问非静态方法
        //System.out.println(id); 在静态方法中不能访问非静态属性
        //System.out.println(this.id); 在静态方法中不能使用this关键字
        System.out.println(sid);
    }

    public static void main(String[] args) {
        // 非静态方法可以用对象名.方法名去调用
        Test t = new Test();
        t.a();
        // 静态方法可以用 类名.方法名 或 对象名.方法名 进行调用
        Test.b();
        t.b();
        
        // 在同一个类中可以直接调用
        b();
    }
}

类的组成

类的组成:属性,方法,构造器,代码块,内部类
代码块分类:普通块,构造块,静态块,同步块
代码:

public class Test {
    // 属性
    int a;
    static int sa;

    // 方法
    public void a() {

    }

    public static void b() {

    }

    // 构造器
    public Test() {
        this.a = a;
    }

    // 代码块
    public void c() {
        System.out.println("aa");
        {
            // 普通块限制了局部变量的作用范围
            System.out.println("-----------------");
            int num = 10;
            System.out.println(num);
        }
        // System.out.println(num);
        // if(){}
        // while(){}
    }


    // 构造块 : 在方法外写代码可在构造块中书写
    {
        System.out.println("---这是构造块");
    }

    // 静态块 : 先于对象存在 ps:在静态块中只能访问静态方法和静态属性
    static {
        System.out.println("这是静态块");
    }
    
}

总结: 代码块执行顺序
最先执行静态块,只在类加载的时候执行一次 (一般用于执行一些全局性的初始化操作)
在执行构造块,(不常用)
在执行构造器,
在执行方法中的普通块

面向对象特性

  1. 封装:
    1. 属性私有化 --> 加入权限修饰符 private 。。。
    2. 提供public方法,提供对属性的获取及修改
  2. 继承:
    1. 提高代码复用性:父类定义的内容,子类可以直接利用,不用代码重复定义
    2. 便于代码的扩展
    3. 是多态的前提
    4. 一个父类可以有多个子类
    5. 一个子类只能有一个直接父类
    6. 继承具有传递性 eg: student --> Person --> Object Object是所有类的根基类

ps: 父类private修饰的内容,子类也继承了,只是因为封装的特性阻碍了直接调用。可用set get方法进行间接调用

权限修饰符

同一个类同一个包子类所有类
private*
default**
protected***
public****

重写

  1. 发生在子类和父类中,当子类对父类提供的方法不满意的时候,要对父类的方法进行重写
  2. 重写有严格的格式要求:
    1. 子类的方法名字和父类必须一致,参数列表(个数,类型,顺序)也要和父类一致。
  3. 重载和重写的区别
    1. 重载:在同一个类中当方法名相同,形参列表不同的时候,多个方法构成了重载
    2. 重写:在不同的类中,子类对父类提供的方法不满意的时候,要对父类的方法进行重写
英文位置修饰符返回值方法名参数抛出异常方法体
重载overload在同一个类中无关无关必须相同必须相同无关不同
重写override子类父类中父类的权限修饰符要低于子类的父类的返回值类型大于子类必须相同必须相同小于等于不同

super

  1. super指的是父类的
  2. super可以修饰属性,可以修饰方法
    在这里插入图片描述
    在特殊情况下,当子类和父类的属性重名时,你要想使用父类的属性,就必须加上修饰符super. 只能用super.方法进行调用,这种情况下super. 就不能省略了。
  3. super修饰构造器
    平时写的空构造器的第一行都有super() --> 作用:调用父类的空构造器,一般省略不写
    (所有构造器的第一行默认情况下都有super() 但是一旦你的构造器中显示的使用super调用了父类构造器,那么这个super()就不会给你默认分配)
    在这里插入图片描述

在构造器中,super调用父类构造器和this调用子类构造器只能存在一个,两者不能共存

在这里插入图片描述
子类创建对象,需要先构造父类

Object类

所有类都直接或间接的继承自Object类,Object类是所有Java类的根基类。
也就意味着所有的Java对象都拥有Object类的属性和方法
如果在类的声明中未使用extends关键字指明父类,则默认继承Object类

  1. Object类的toString方法
    在这里插入图片描述
    在这里插入图片描述
    出现问题:子类student对父类Object提供的toString方法 不友好,子类可以重写父类的toString 方法

  2. Object的equals方法
    在这里插入图片描述

在这里插入图片描述

总结:
equals作用: 这个方法提供了对对象内容是否相等的一个比较方式,对象的内容指的就是属性
父类Object提供的equals就是在比较==地址,没有实际意义,我们一般不会直接使用父类提供的方法,而是在子类中对这个方法进行重写

重写equals 代码
在这里插入图片描述
在这里插入图片描述

类与类之间的关系

总结:

  1. 面向对象的思维:找参与者,男孩类 女孩类
  2. 体会了什么叫方法的实参,什么叫方法的形参
    在这里插入图片描述
    具体传入的内容实参
    在这里插入图片描述
  3. 类和类可以产生关系
    1. 将一个类作为另一个类中的方法的形参
    2. 将一个类作为另一个类的属性
public class Boy {
    int age;
    String name;

    public void buy(){
        System.out.println("make friends buy buy buy");
    }

    public Boy(int age, String name) {
        this.age = age;
        this.name = name;
    }
}
public class Girl {
    String name;
    double weight;
    Mother m /*= new Mother()*/;

    public Girl(String name, double weight) {
        this.name = name;
        this.weight = weight;
    }

    public void love(Boy boy){ // 引用数据类型
        System.out.println("my boy friend name is " + boy.name + "my boy friends age is " + boy.age);
        boy.buy();
    }

    public void wechat(){
        m.say();
    }

    public void add(int a ){ // 基本数据类型
        System.out.println(a);
    }
}
public class Mother {
    public void say(){
        System.out.println("say something full of love");
    }
}
public class Test {
    public static void main(String[] args) {
        Boy ansan = new Boy(30, "安三");
        Girl lili = new Girl("丽丽",100);
        lili.love(ansan);
        lili.m = new Mother();
        lili.wechat();
    }
}

多态 (多种状态)

  1. 多态与属性无关,多态指的是方法的多态,而不是属性的多态
    案例带入: 小女孩和动物玩
public class Animal {
    public void shout(){
        System.out.println("i am animal i can shout");
    }
}
public class Cat extends Animal {
    // 喊叫方法
    public void shout(){
        System.out.println("i am a cat  miao~ miao~ ");
    }

    public void scratch(){
        System.out.println("i am a cat i can scratch people");
    }
}
public class Dog extends Animal {
    public void shoot(){
        System.out.println("i am a doy wang wang ~");
    }

    public void guard(){
        System.out.println(" i am a doy i can protect someone");
    }
}
public class Girl {
    // play with cat
    /*public void play(Cat cat){
        cat.shout();
    }

    public void play(Dog dog){
        dog.shoot();
    }*/

    public void play(Animal animal){
        animal.shout();
    }
}
public class Test {
    public static void main(String[] args) {
        Girl girl = new Girl();
        //Cat cat = new Cat();
        // 小女孩与猫玩耍
        //girl.play(cat);
        //Dog dog = new Dog();
        //girl.play(dog);

        Animal an = new Animal();
        Animal an1 = new Cat();
        Animal an2 = new Dog();
        girl.play(an);
        girl.play(an1);
        girl.play(an2);

    }
}

总结:

  1. 现有父类,再有子类 --> 继承 先有子类,再有父类 --> 泛化
  2. 什么是多态?
    1. 多态就是多种状态:同一个行为,不同的子类表现出来不同的形态
    2. 态指的就是同一个方法调用,由于对象不同,会产生不同的行为
  3. 多态的好处: 提高代码的扩展性,符合面向对象的设计原则:开闭原则
    开闭原则指的是:扩展是开放的,修改是关闭的(修改代码)
    (多态可以提高扩展性,但是扩展性没有达到最好,以后会学习到反射)
  4. 多态的要素:
    1. 继承:Cat extends Animal , Dog extends Animal
    2. 重写:子类对父类的方法shout()重写
    3. 父类引用指向子类对象 Animal an2 = new Dog();
      Animal an2 = new Dog();
      =左侧 : 编译期的类型
      =右侧: 运行期的类型

Animal an = new Dog();
g.play(an);
在这里插入图片描述
上面的代码,也是多态的一种常见场合:父类当方法的形参,然后传入的是具体子类的对象,
然后调用同一个方法,根据传入的子类的不同展现出来的效果也不同,构成了多态

在这里插入图片描述
向上转型,向下转型
在这里插入图片描述
在这里插入图片描述

简单工厂模式

多态的另一种应用:
不仅可以使用父类做方法的形参,也可以使用父类做方法的返回值类型,真实返回的对象可以是该类的任意一个子类对象。
简单工厂模式的实现,它是解决大量对象创建问题的一个解决方案。将创建和使用分开,工厂负责创建,使用者直接调用即可。
简单工厂模式的基本要求是:
1.定义一个static方法,通过类名直接调用
2.返回值类型是父类类型,返回的可以是其任意子类类型
3.传入一个字符串类型的参数,工厂根据参数创建对应的子类产品

public class Test {
    public static void main(String[] args) {
        Girl girl = new Girl();
        /*Animal an2 = new Dog();*/
        Animal dog = PetStore.getAnimal("狗");
        girl.play(dog);

    }
}
public class PetStore { // 宠物店
    // 方法:提供动物
    public static Animal getAnimal(String petName){ // 多态的应用场合:父类当作方法的返回值
        Animal an = null ;

        if("猫".equals(petName)){ // petName.equals("猫") --> 这样写容易发生空指针异常
            an = new Cat();
        }

        if("狗".equals(petName)){
            an = new Dog();
        }

        return an;
    }
}

final 修饰符

  1. final修饰变量
public class Test {
    public static void main(String[] args) {
        // 第一种情况
        // final 修饰变量,变量的值不可以改变,这个变量也变成了一个字符常量 , 约定俗称的规定:名字大写
        final int A = 10; // final修饰基本数据类型
        //A = 20; 报错

        // 第二种情况
        // final 修饰引用类型,地址值不能改变,
        final Dog dog = new Dog();
        //dog = new Dog(); 报错
        // dog对象的属性,依然可以改变
        dog.age = 10;
        dog.weight = 13.7;

        // 第三种情况
        final Dog d2 = new Dog();
        a(d2);

        // 第四种情况
        b(d2);

    }

    public static void a (Dog d){
        d = new Dog();
    }

    public static void b(final Dog d){ // d 被final修饰 地址不可改变
        // d = new Dog(); 报错
    }
}
  1. final修饰方法
    final 修饰的方法,子类不能进行重写
    在这里插入图片描述
    final 修饰类,代表没有子类,该类不能被继承:
    一旦一个类被final修饰,那么里面的方法也没必要用final修饰了(final可省略不写)
    在这里插入图片描述
    案例: JDK提供的Math类.
    在这里插入图片描述
    1.Math类没有子类,不能被其他类继承
    2.Math类中的属性全部被final修饰,方法也是被final修饰的,只是省略不写了
    原因:子类没有必要进行重写
    4.外界不可以创建对象
    Math m = new Math();
    在这里插入图片描述
    5.发现Math类中的所有的属性,方法都被static修饰,那么不用创建对象去调用,而是可以利用类名直接调用

抽象类 抽象方法

抽象类和抽象方法的关系:
1.抽象类中可以定义 0 ~ n 个抽象方法

抽象类作用:
1.在抽象类中定义抽象方法,为子类提供一个通用的模板,子类可以在模板的基础上进行开发,先重写父类的抽象方法,然后可以扩展子类自己的内容,抽象类设计避免了子类设计的随意性,通过抽象类,子类的设计变得更加严格,进行某些程度上的限制。

// 4. 一个类中,如果有方法是抽象方法,那么这个类也要变成一个抽象类
// 5. 一个抽象类中可以有 0~n 个抽象方法
public abstract class Person {

    // 1.在一个类中,会有一类方法,子类对这个方法非常满意,无需重写,直接使用
    public void eat(){
        System.out.println("一顿不吃饿得慌");
    }

    // 2. 在一个类中,会有一类方法,子类对这个方法永远不满意,会对这个方法进行重写
    // 3. 一个方法的方法体去掉,被abstract修饰,这个方法就变成了一个抽象方法
    public abstract void say();
}

// 6.抽象类可以被其他类继承
// 7. 一个类继承抽象类,那么这个类可以变成抽象类
// 8. 一般子类不会加abstract修饰,一般会让子类重写父类中的抽象方法
// 9. 子类继承抽象类,就必须重写全部的抽象方法
// 10. 子类如果没有重写父类全部的抽象方法,那么子类也可以变成一个抽象类
class Student extends Person{
    @Override
    public void say() {
        System.out.println("xxxxx");
    }
}

class Demo{
    public static void main(String[] args) {
        // 11. 创建抽象类的对象 --》 抽象类不可以创建对象
        //Person p = new Person();

        // 12. 创建子类对象
        Student s = new Student();
        s.say();
        s.eat();

        // 13. 多态写法:父类引用指向子类对象
        Person p = new Student();
    }
}

面试题
1.抽象类不能创建对象,那么抽象类中是否有构造器?
抽象类中一定有构造器。构造器的作用,给子类初始化对象的时候要先super调用父类构造器 。
2.抽象类是否可以被final修饰
不能被final修饰,因为抽象类设计的初衷就是给子类继承用的。要是被final修饰了这个抽象类,就不存在继承,没有子类。

接口

/**
 * 1.类是类,接口是接口,他们是同一个层次的概念
 * 2.接口中没有构造器
 * 3.接口如何声明: interface
 * 4.在jdk1.8之前,接口中只有两部分内容
 * (1) 常量; 固定修饰符: public static final
 * (2) 抽象方法: 固定修饰符 public abstract
 */
public interface TestInterface01 {

    // 常量
    public static final int NUM = 10;

    // 抽象方法
    public abstract void a();

    public abstract void b(int num);

    public abstract void c(String name);
}

interface TestInterface02{
    void a1();
}

/*
 5. 和接口的关系是什么? 实现关系 类实现接口
 6. 一旦实现一个接口,那么实现类要重写接口中的全部抽象方法
 7. 如果没有全部重写抽象方法,那么这个类可以变成一个抽象类
 8. java只有单继承,java还有多实现
一个类继承其他类,只能直接继承一个父类
但是实现类实现接口的话,可以实现多个接口
 9. 写法: 先继承,在实现
 */
class Student extends Person implements TestInterface01,TestInterface02{
    @Override
    public void a() {

    }

    @Override
    public void b(int num) {

    }

    @Override
    public void c(String name) {

    }

    @Override
    public void a1() {

    }
}

class Test{
    public static void main(String[] args) {
        // 10. 接口不能创建对象
        //TestInterface02 in2 = new TestInterface02()
        TestInterface02 t = new Student(); // 多态的应用 接口指向实现类

        // 11.接口中常量如何访问
        System.out.println(TestInterface01.NUM);
        System.out.println(Student.NUM);
        Student student = new Student();
        System.out.println(student.NUM);
        TestInterface01 t1 = new Student();
        System.out.println(t1.NUM);
    }
}

接口的作用是什么?
定义规则: 只是跟抽象类不同地方在于哪?它是接口不是类
接口定义好规则之后,实现类负责实现即可

继承与实现的应用案例

继承: 子类对父类的继承
实现: 实现类对接口的实现

案例:
手机 是不是 照相机
继承: 手机 extends 照相机 ”is -a “ 的关系,手机是一个照相机 so 利用继承的写法不好

实现: 手机 implements 拍照功能 "has - a "的关系,手机具备照相的能力

案例:
飞机 小鸟 风筝
定义一个接口 : Flyable

多态的应用场合
1.父类当作方法的形参,传入具体的子类的对象
2.父类当作方法的返回值,返回值是具体的子类对象
3.接口当作方法的形参,传入具体的实现类的对象
4.接口当作方法的返回值,返回的是具体的实现类的对象

jdk1.8之后,新增非抽象方法
1.被public default修饰的非抽象方法:
注意1:default修饰符必须要添加,否则出错
注意2:实现类中要是想重写接口中的非抽象方法,那么default修饰符必须不能加,否则出错
2.静态方法
注意1: static 不可以省略不写
注意2: static 修饰的方法不可以被重写

public interface TestInterface2 {
    // 常量
    public static final int NUM=0;
    // 抽象方法
    public abstract void a();
    // public default 非抽象方法
    public default void b(){
        System.out.println("-----TestInterface2 ---- b");
    }
    // 静态方法
    public static void c(){
        System.out.println("-----TestInterface2 ----- static ");
    }
}
class Demo implements TestInterface2{
    @Override
    public void a() {
        System.out.println("重写了a方法");
    }

    public static void c(){
        System.out.println("Demo中的静态方法");
    }
}

class A {
    public static void main(String[] args) {
        Demo demo = new Demo();
        demo.c();
        TestInterface2.c();
    }
}

疑问? 为什么jdk1.8之后要在接口中加入非抽象方法
如果接口中只能定义抽象方法的话,那么我们要是修改接口中的内容,那么对实现类的影响太大了,所有实现类都会受影响,所以1.8之后在接口中加入了非抽象方法,避免对实现类的影响

public interface TestInterface {
    void a();
    void b();
    public default void c(){

    }
}

class Test01 implements TestInterface{
    @Override
    public void a() {

    }

    @Override
    public void b() {

    }
}

class Test02 implements TestInterface{
    @Override
    public void a() {

    }

    @Override
    public void b() {

    }
}

内部类

成员内部类

/**
 * 1. 类的组成: 属性  方法  构造器  代码块 (普通块 静态块 构造块 同步块) 内部类
 * 2. 一个类TestOuter的内部的类SubTest叫内部类  外部类:TestOuter
 * 3. 内部类 : 成员内部类(静态的 非静态的)  和   局部内部类 (位置:方法内,块内,构造器内)
 * 4. 成员内部类:
 *         里面属性,方法,构造器等
 *         修饰符: private default protect public final abstract
 */
public class TestOuter {

    // 非静态的成员内部类
    class D {
        int age = 20;
        String name;
        public void method(){
            // 5. 内部类可以访问外部类的内容
            /*System.out.println(age);
            a();*/
            int age = 30;

            // 8. 内部类和外部类属性重名的时候如何进行调用
            System.out.println(age);// 30
            System.out.println(this.age);// 20
            System.out.println(TestOuter.this.age);// 10
        }
    }

    // 静态成员内部类
    static class E {
        public void method(){
            // 6. 静态内部类中只能访问外部类中static修饰的内容
            //System.out.println(age);
            //a();
        }
    }

    // 属性
    int age = 10 ;

    // 方法
    public void a() {
        System.out.println("这是a方法");
        {
            System.out.println("这是一个普通块");
            class B {

            }
        }
        class A {

        }

        // 7. 外部类想要访问内部类的东西,需要创建内部类的对象,然后进行调用
        D d = new D();
        d.method();
    }

    static {
        System.out.println("这是静态块");
    }

    {
        System.out.println("这是构造块");
    }

    // 构造器
    public TestOuter() {
        class C {

        }
    }
}

class Demo {
    public static void main(String[] args) {
        // 8.创建外部类的对象
        TestOuter testOuter = new TestOuter();
        testOuter.a();

        // 9.创建内部类的对象
        // 静态的成员内部类创建对象
        TestOuter.E e = new TestOuter.E();
        // 非静态成员内部类创建对象
        // 错误  new TestOuter().D
        TestOuter t = new TestOuter();
        TestOuter.D d = t.new D();

    }
}

局部内部类

public class TestOuter {
    public void method() {
        //1. 在局部内部类中访问到的变量,必须是被final修饰的
        final int num = 10;
        class A {
            public void a() {
                // num = 20;
                System.out.println(num);
            }
        }
    }

    // 如果类B 在整个项目中只使用一次,那么就没有必要单独创建一个B类。使用内部类就可以了
    public Comparable method2() {
        class B implements Comparable {
            @Override
            public int compareTo(Object o) {
                return 0;
            }
        }
        return new B();
    }


    public Comparable method3() {
        // 匿名内部类
        return new Comparable() {
            @Override
            public int compareTo(Object o) {
                return 0;
            }
        };
    }

    public void teat(){
        Comparable com = new Comparable() {
            @Override
            public int compareTo(Object o) {
                return 0;
            }
        };
        System.out.println(com.compareTo("abc"));
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值