Java类和对象

Java 类和对象

区分面向过程与面向对象

  1. 面向过程:将一个问题按步骤列举,然后一个人从第一步做到最后一步(从始至终一个人)
  2. 面向对象:将一个问题分解成若干个问题,每个对象负责一个或多个问题,最后也达到解决问题的效果(多个人共同完成)

类的定义与使用

一个类就像是根据图纸造房子(一张图纸可以造成多个房子,一个类也可以实例化多个对象,每个对象都有自己的空间)

定义

类前面的修饰符只能是 默认public,且一个文件中只能有一个public修饰的类

// 创建类:类名一般采用大驼峰命名
public class ClassName{ 
	field; // 字段(属性)
	method; // 行为
}

使用

public class PetDog {
	// 属性
	public String name;//名字
	public String color;//颜色 
	
	// 行为
	public void barks() { 
		System.out.println(name + ": 旺旺旺~~~"); 
	}
	public void wag() { 
		System.out.println(name + ": 摇尾巴~~~"); 
	} 
}

对象实例化

通过 new 实例化对象(会在堆上给对象分配空间)

public Main {
	public void static main(String[] args) {
		PetDog dog1 = new PetDog();  // 对象实例化
		PetDog dog2 = new PetDog();
		
		// 对象.方法名/属性名   进行访问对象中的方法和属性
		dog1.barks();
		dog1.name;
	}
}

一个简略图,实例化时,引用变量会引用堆上开辟好的一块空间地址(像成员变量就存储在堆上,每个对象的成员变量是自己独有的;而方法和类的某些属性,是共享的,存在方法区,static处详细讲)

在这里插入图片描述

this 引用

当类中方法接收参数的时候,若形参名字和成员变量名字相同,会优先使用形参,这个时候就需要用this来区分了,this其实就是类方法形参中的第一个参数(默认可以不写:类名 this),所以建议使用成员变量的时候都加 this

image-20220331174745252
在这里插入图片描述

this总结

  1. this 就是当前对象的引用 ---- 谁调用方法或字段就是谁
  2. 当方法参数和字段名字相同时,可以以此来区分
  3. this 是一个隐藏参数(第一个参数) – 看上面的图
  4. 访问成员变量时,建议加上 this
  5. this 只能在成员方法中使用,不能在静态方法中,static中会讲为什么
  6. this(); 调用构造方法,只能放在构造方法中

实例化过程

  1. 加载 类对应的字节码文件
  2. 在堆上分配内存空间
  3. 若有默认初始化,进行默认初始化(定义类的时候进行的赋值
  4. 调用合适的构造方法(构造方法可以重载)
  5. 返回堆上初始化好的空间的地址

构造方法

构造方法的名字与类名相同,但没有返回值
若自己不写,系统会自动提供一个 不带参数的空构造方法
若自己实现了一个或多个构造方法时,系统就不会自动提供了

// 空构造方法(自己没有实现。系统会提供一个空的构造方法)
public PetDog() {
	this("小黑");  // 可以在构造方法中,调用其他构造方法(1.必须第一行;2. 不能形成环)	
}

// 带一个参数的构造方法
public PetDog(String name) {
	this.name = name;  // this后面有讲解
}
方法的重载

上面两个构造方法就可以同时存在代码中,这就构成了方法的重载

  1. 方法名相同
  2. 参数列表不同
  3. 返回值不做要求
方法签名

经过编译器编译修改过之后方法最终的名字。具体方式:方法全路径名+参数列表+返回值类型,构成方法完整的名字(就是为了区分重载的方法)

在这里插入图片描述
在这里插入图片描述

四大访问权限

不同的只是访问权限不同(限制使用范围)

  1. privte(私有权限)
  2. 默认权限(也叫做:包访问权限)
  3. protected:一般建立在继承的基础上使用(继承中讲解)
  4. public(公共权限)

作用范围

在这里插入图片描述

public

public的访问权限最大,在哪里都可以访问,因此及其不安全,在后面的开发过程中还是需要合理的使用 访问修饰符

private

Java面向对象的封装机制,主要体现在private 关键字上
运用private关键字对类的实现细节进行隐藏,类中提供公开的接口来间接访问 private修饰的属性,这种机制就叫做 “封装”,降低了代码的耦合度,数据更加的安全

在这里插入图片描述

默认权限(包权限)

在面向对象体系中,提出了一个软件包的概念,即:为了更好的管理类,把多个类收集在一起成为一组,称为软件包。类似于 我们常见的 “文件夹”
包还有一个重要的作用:在同一个工程中允许存在相同名称的类,只要处在不同的包中即可

如何导入包

packet + 路径(以进行分隔) packet com.bit.demo 声明包(不是系统自带的,一般都会需要进行声明包 (使用自己创建包里面的类))

  1. 系统提供的
    • import java.util.Arrays — 导入java.util 包里面的Array类
    • import java.util.* ---- 导入java.util包里面的所有类(后面程序在用到里面类的时候才加载包里面的对应的类,不会直接全部加载
    • 不导入包:java.util.Data 也可以表示 Data类(但是若同时 需要 util包 和 sql包时,就只能使用 java.util.Data来区分了,因为util 和 sql 中都包含 Data类
  2. 自定义的:import 路径. import 路径.方法名*
  3. 静态导入:类是个静态方法,就可以静态导入包,直接使用字段和方法了 (了解即可,不推荐
    1. 例如:import static java.lang.Math.* 直接sqrt(x)即可;若不导入需要Math.sqrt(x)
    2. import static java.lang.System.* 导入后使用: out.println() ;若没导入需要:System.out,println()

static

static 修饰的变量 或 方法 称之为 静态变量 / 方法 , 他们都是存放在方法区的(方法区的东西只有一份,也就是同一个类创建出来的多个对象会 共用 方法区 上分配的资源,一个对象修改了静态变量的值,另一个对象访问的时候也会随之更改

注意点

  1. 普通方法中是可以访问静态成员变量的
  2. 静态成员方法中不可以访问普通成员变量
    • 普通成员变量依赖于对象,而静态变量是用类进行调用的 - 不依赖对象
  3. 静态变量可以通过类访问,也可以通过对象访问;但推荐用类访问
  4. 生命周期随类的创建而创建,销毁而销毁
  5. 静态方法没有 隐藏this 参数 this指代当前对象的引用,而静态方法不依赖对象

示例

public static void main(String[] args) {
    // classes是静态属性
        Student student1 = null;  // student1不指向任何对象
        student1.classes = "104班";  // classes 不依赖任何对象
        System.out.println(student1.classes);   // 并不会报 空指针异常
    }

代码块执行顺序

见下图,我们可以发现,在字节码文件中,实例代码块被默认的加载到了构造方法里面,并放在开头;且当我们创建多个对象时,静态代码块只会执行一次


顺序:先 静态代码块,再 实例代码块,最后 构造方法

在这里插入图片描述

内部类

  1. 之所以被叫做 内部类,是因为其 定义包含在某个类当中(类的一个成员)
  2. 内部类和外部类共用同一个java源文件,但是经过编译之后,内部类会形成单独的字节码文件

实例内部类

  1. 如何实例化内部类对象:
    • 部类名.内部类名 变量 = 外部类对象的引用.new InnerClass()
  2. 实例内部类当中 不能定义静态的成员变量 - 如果要定义 必须是编译的时候确定的值(必须是static final的)
    • 静态变量不依赖于对象,但实例内部类依赖于外部类的对象
    • public static final int data6 = 60; 常量–》编译的时候,就确定了data6的值
  3. 实例内部类当中,若内部类的成员变量与外部类成员变量重名了,优先使用自己的
    • 使用外部类的:OuterClass.this.变量名
    • 使用内部类的:this.变量名 / 直接变量名
  4. 实例内部类中不能包含静态方法
  5. 外部类不能直接访问内部类对象,必须创建内部类对象
class OuterClass {
    // 实例成员变量/普通成员变量
    public int data1 = 10;
    public int data2 = 20;
    public static int data3 = 30;

    // 实例内部类
    class InnerClass {
        public int data4 = 40;
        public int data5 = 50;
        public int data3 = 1;
        public static final int data6 = 60;

        /**
         * 构造方法
         */
        public InnerClass() {
            System.out.println("InnerClass的构造方法");
        }

        public void method() {
            System.out.println(data3);
            System.out.println(OuterClass.data3);
            System.out.println("InnerClass的一个method方法");
            System.out.println(OuterClass.this.data1);  // 访问外部类的成员(固定写法)
            System.out.println(this.data4);  // 当前类的成员,this--内部类的对象引用
            System.out.println(data6); // 内部类里面的static final常量
        }
    }

    public void methodOut() {
        InnerClass innerClass = new InnerClass();
        System.out.println(innerClass.data4);  // 访问内部类的成员 - 必须要有内部类对象
        System.out.println(this.data1); // 访问自己的成员
    }
}

public class TestDemo {
    public static void main(String[] args) {
        // 构造内部类对象
        OuterClass outerClass = new OuterClass();
        System.out.println(outerClass.data1);

        // 外部类名.内部类名  变量  =  外部类对象的引用.new InnerClass()
        OuterClass.InnerClass innerClass1 = outerClass.new InnerClass();

        // new OuterClass():匿名对象
        OuterClass.InnerClass innerClass2 = new OuterClass().new InnerClass();
        System.out.println(innerClass1.data4);
        System.out.println(innerClass2.data4);
        innerClass1.method();

    }
}

静态内部类

实例内部类更加的常用,因为它的创建不需要借助外部类对象

  1. 如何实例静态内部类
    • OuterClass1.InnerClass innerClass = new OuterClass1.InnerClass();
  2. 在静态内部类当中,只能访问自己的成员 或 外部类的静态成员
    • 如何访问外部类的其他成员?(获取外部类对象)
      1. 手动在静态内部类中创建(new OuterClass)
      2. 通过构造方法 外部 传入
  3. 静态内部类里面可以定义静态方法和静态变量
class OuterClass1 {
    public int data1 = 10;
    private int data2 = 20;
    public static int data3 = 30;

    public void method() {
        System.out.println("OuterClass2::method()");
    }

    // 静态内部类
    static class InnerClass {
        public int data4 = 40;
        private int data5 = 50;
        public  static int data6 = 60;

        // 1. 创建
        // OuterClass1 out = new OuterClass1();

		// 2. 外部传入
		OuterClass1 out;
        public InnerClass(OuterClass1 out) {
            this.out = out;
            System.out.println("InnerClass()");
        }

        public InnerClass() {
            System.out.println("InnerClass()");
        }

        public void method() {
            System.out.println(out.data1);
            System.out.println(data4);
            System.out.println("innerclass的method方法");
        }

        public static void fun() {
            System.out.println("haha");
        }
    }
}

public class TestDemo1 {

    public static void main1(String[] args) {
        // 不需要创建外部类对象,所以静态内部类比实例内部类使用频繁(链表的节点就可以使用静态内部类)
        OuterClass1.InnerClass innerClass = new OuterClass1.InnerClass();
        OuterClass1.InnerClass innerClass1 = new OuterClass1.InnerClass(new OuterClass());
        innerClass.method();
    }
}

匿名内部类

在线程的创建中会用到

class Test {
    public int a = 10;

    public void test() {
        System.out.println("123");
    }
}

public class TestDemo2 {
    public static void main(String[] args) {
        // 匿名内部类
        new Test(){
            public void test1() {
                System.out.println("hehe");
            }

            @Override
            public void test() {
                System.out.println("haha");
            }
        }.test();
    }
}

继承

Java不支持多继承,因此在后面出现了 接口的概念,来解决多继承问题

class Animal {
    String name;
    String hair;
    
    public void eat() {
        System.out.println("吃饭");
    }
}

class Cat extends Animal {
    public void miu() {
        System.out.println("Cat:miumiu");
    }
}

class Dog extends Animal {
    public void bark() {
        System.out.println("Dog:bark");
    }
}

super

  1. super 关键字的作用:在子类中访问父类的成员
  2. 只能在非静态方法中使用,见子类构造方法(静态方法不依赖对象,也就不执行构造方法)

在这里插入图片描述

子类构造方法

若自己没有写,编译器也会提供一个无参数的构造方法,并在构造子类之前,先调用 super来构造父类(若自己在子类中定义了带参数的构造方法时,1. 调用super(),2.实现super的构造方法)
注意:子类构造方法中,super()也是必须放在第一行的,因此super() 与 this() 不能一起使用

在这里插入图片描述

super与this
相同点
  1. 只能在类的非静态方法中使用,用来访问非静态成员方法和字段
  2. 在构造方法中调用时,必须是构造方法中的第一条语句,并且不能同时存在
不同点
  1. this 是当前对象的引用;super 是由子类对象 从 父类对象 中继承过来的那部分的"引用"
  2. 在非静态成员方法中,this用来访问本类的方法和属性,super用来访问父类继承下来的方法和属性
  3. this是非静态成员方法的一个隐藏参数,super不是隐藏的参数
  4. 在构造方法中:this(…)用于调用本类构造方法,super(…)用于调用父类构造方法,两种调用不能同时在构造方法中出现
  5. 构造方法中一定会存在super(…)的调用,用户没有写编译器也会增加,但是this(…)用户不写则没有

带继承的代码块执行顺序

根据下图得出结论:

  1. 最早执行的是静态代码块,且父类 静态代码块 早于 子类
  2. 父类实例代码块 和 构造方法 紧接着执行
  3. 子类实例代码块 和 构造方法在执行
  4. 第二次实例化子类时,父子类的静态代码块 都不会执行了
class Animal {
    String name;

    {
        System.out.println("Animal 实例代码块");
    }

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

    public Animal(String name) {
        this.name = name;
        System.out.println("Animal 构造方法");
    }
}

class Cat extends Animal {

    {
        System.out.println("Cat 实例代码块");
    }

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

    public Cat(String name) {
        super(name);
        System.out.println("Cat 构造方法");
    }
}


public class Demo2 {
    public static void main(String[] args) {
        Cat cat1 = new Cat("咬人猫");
        System.out.println("===================");
        Cat cat2 = new Cat("食人花");
    }

}

在这里插入图片描述

protected

注意看:两个类处于不同包地下

在这里插入图片描述

final

final关键可以用来修饰变量、成员方法以及类

  1. 修饰变量或字段,表示常量(即不能修改)
  2. 修饰类:表示此类不能被继承
  3. 修饰方法:表示该方法不能被重写(重写在多态中介绍)

多态

是一种思想,
去完成某个行为,当不同的对象去完成时会产生出不同 的状态。

在这里插入图片描述

前提条件

必须发生在继承体系下

  1. 向上转型:父类引用 引用子类对象
  2. 发生重写:父类和子类当中有同名的覆盖方法
  3. 通过父类引用,调用这个同名的方法。此时在程序执行时,会发生动态绑定

向上转型与向下转型

向上转型

使用 父类对象 来 引用子类对象(后面发生动态绑定)

  1. 向上转型的优点:让代码实现更简单灵活。
  2. 向上转型的缺陷:不能调用到子类特有的方法。
  1. 直接赋值
Animal animal = new Cat();
  1. 参数列表
public static void eatFood(Animal a){ 
	a.eat(); 
}

eatFood(new Cat());
  1. 返回值
public static Animal buyAnimal(String var){ 
	if("狗" == var){ 
		return new Dog(); 
	} else if("猫" == var){ 
		return new Cat(); 
	} else{ 
		return null; 
	} 
}
向下转型

向下转型不安全,万一转换失败,运行时就会抛异常。Java中为了提高向下转型的安全性,引入了 instanceof ,如果该表达式为true,则可以安全转换

Java操作数据库的JDBC标准中,会用到向下转型

Cat cat = new Cat();
Animal animal = cat;
  
if(animal instanceof Cat) {
    cat = (Cat)animal;  // 向下转型
    cat.mew();
}

重写

基本要求
  1. 方法名相同
  2. 参数列表相同
  3. 返回值相同(JDK7以后,返回值是 父子类 关系,也构成重写)
注意点
  1. 访问权限不能比父类中被重写的方法的访问权限更低。例如:如果父类方法被public修饰,则子类中重写该方法就不能声明为 protected
  2. 父类中被 static,private,final 修饰的方法不能被重写
  3. 重写的方法, 可以使用 @Override 注解来显式指定. 有了这个注解编辑器能帮我们进行一些合法性校验.
重写与重载区别
区别点重载(override)重写(override)
参数列表必须修改一定不能修改
返回类型可以修改一定不能修改
访问限定符可以修改大部分不能(子类的 >= 父类的就行)

多态优缺点

  1. 降低 “圈复杂度”,也就是 避免大量的 if–else
class Shape {

    public void draw() {
        System.out.println("画图形");
    }
}

class Cycle extends Shape {
    @Override
    public void draw() {
        System.out.println("●");
    }
}

class Rect extends Shape {
    @Override
    public void draw() {
        System.out.println("♦");
    }
}

class Flower extends Shape {
    @Override
    public void draw() {
        System.out.println("✿");
    }
}

// 不使用多态
public static void drawMaps1() {
        Cycle cycle = new Cycle();
        Rect rect = new Rect();
        Flower flower = new Flower();
        String[] shapes = {"cycle","rect","cycle","rect","flower"};
        for (String shape: shapes) {
            if(shape.equals("cycle")) {
                cycle.draw();
            } else if(shape.equals("rect")) {
                rect.draw();
            } else if(shape.equals("flower")) {
                flower.draw();
            }
        }
    }

// 使用多态
public static void drawMaps() {
    Cycle cycle = new Cycle();
     Rect rect = new Rect();
     Flower flower = new Flower();
     Shape[] shapes = {cycle,rect,cycle,rect,flower};
     for (Shape shape : shapes) {
         shape.draw();
     }
 }
  1. 提高可扩展性

直接继承 Shape 类,就可以画 三角形 了

class Triangle extends Shape {
    @Override
    public void draw() {
        System.out.println("▲");
    }
}
  1. 代码的运行效率降低。

注意:

运行结果:D.func() 0

class B {
    public B() {
        // do nothing
        func();
    }

    public void func() {
        System.out.println("B.func()");
    }
}
class D extends B {
	private int num = 1;
    @Override
    public void func() {
        System.out.println("D.func() " + num);
    }
}

public class Demo4 {
    public static void main(String[] args) {
        D d = new D();
    }
}
分析

虽然发生了动态绑定,但是它并不属于 “多态”

  • 构造 D 对象的同时, 会调用 B 的构造方法.
  • B 的构造方法中调用了 func 方法, 此时会触发动态绑定, 会调用到 D 中的 func
  • 此时 D 对象自身还没有构造, 此时 num 处在未初始化的状态, 值为 0

抽象类

当一个类里面有了抽象方法时,此时这个类就要变成抽象类:只是这个类不能被实例化而已,里面还是可以定义成员变量和成员方法的

  1. 抽象类一般都是用来被继承的
  2. 抽象类不能进行实例化
  3. 当一个普通类继承抽象类时,必须重写抽象类中全部抽象方法
  4. 抽象类存在的最大意义 就是为了继承
  5. 抽象类也可以发生向上转型,这样就可以发生多态
  6. 当一个抽象类A 继承 抽象类B时,此时抽象类A可以不重写抽象类B中的抽象方法
  7. 当一个普通类C 继承了 6 中的抽象类A 时,就得重写以往类中所有的抽象方法(A,B)中的
  8. final 和 abstract 两个关键字不能一起使用
  9. 抽象方法也不能是 private
  10. 抽象类当中不一定有抽象方法,但若有抽象方法,这个类必然是抽象类
  11. 抽象方法一般都不需要实现内容

接口

  1. 接口当中的成员变量,默认都是 public static final的,必须被赋值
  2. 接口当中的成员方法,若需要实现,需要用default来修饰
  3. 接口中的静态方法可以直接使用的
  4. 接口不能进行实例化,只能被继承
  5. 继承接口的方法中,必须重写抽象方法,默认方法可以重写,也可以不重写
  6. 接口 . 静态方法() – 调用静态方法
  7. 和抽象类中的 6,7一样
  8. 一个普通类可以 implements 多个接口,接口间用 逗号隔开
  9. A接口 extends B接口 ----> A接口扩展了B接口的功能

三大常用接口

使用Comparable 和 Comparator 两个接口来排序时,必须时引用类型数组,int[ ] 不可以,Integer[ ] 可以

Comparable

缺点:一个类里面只能有一个compareTo方法,当你第一次排序 name 字段,但是接下来想看看通过 score 比较的结果,这个时候就需要改变 Student 类中的实现了;这时候 Comparator 就可以解决

在这里插入图片描述
在这里插入图片描述

Comparator

创建一个类,实现 Comparator 比较器接口,直接传入 Arrays.sort()中即可

在这里插入图片描述

Cloneable
浅拷贝

在这里插入图片描述

深拷贝

当在一个对象中存在引用对象的成员属性事,除了拷贝外面一层对象外,里面引用的对象也需要被拷贝

在这里插入图片描述

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值