Java-02 面向对象2

1. this/super关键字

        this:Java中的关键字,代表调用当前类中实例变量与实例方法的那个对象(this即对象本身)。this中存储的是对象的引用(堆内存中的地址),可以将this理解为指向对象本身的指针。

       this的用法(3种):

                1)直接引用(多用于以对象作为参数传递):

                        可以直接通过this.xxx直接调用对象的实例方法与实例变量。

                        this代表对象本身,当考虑到需要传递当前对象时,可以使用this。

/**
*	@description Eat Apple
*	@author Rock
*	@date 2021/07/01
*/

public class AppleClear{
	// Clear Apple
	public static Apple clearApple(Apple apple){
		System.out.println("已清洗苹果");
	}
	
	// main(): 完成吃苹果的动作
	public static void main(String[] args){
		Apple newApple = new Apple();
		newApple.eatApple();
	}
	
	// Apple class
	class Apple{
		// 无参构造
		public Apple(){}
		
		// eat Apple
		public void eatApple(){
			// 清洗苹果
			clearApple(this);
			// 吃苹果
			System.out.println("已吃完苹果");
		}
	}
}

                        以上实例中,创建Apple类的对象newApple后,在实例方法eatApple()中通过this关键字将当前调用该方法的对象(newApple)传递给了静态方法clearApple()。
        

               2)形参名与成员变量名重复时通过this区分(构造方法/实例方法):

                        当调用方法的形参名与实例变量的名字重复时,通过this.实例变量名标注以区分。默认情况下,this是可以省略的。

public class BankAccount{
	// 实例变量
	int account_id;
	String userName;
	
	// 构造方法
	public BankAccount(int account_id, String userName){
		this.account_id = account_id;
		this.userName = userName;
	}
	
	// set()
	public void setAccount_id(int account_id){
		this.account_id = account_id;
	}
	
	// set()
	public void setUserName(String userName){
		this.userName = userName;
	}
}

                        以上实例中,在构造方法与实例方法set中,通过this关键字标识了实例变量,用以区分形参(否则编译器无法识别)。

                3)在构造方法内部通过this(参数列表)调用同类中形参列表匹配的构造方法:

                        这个和super()调用超类构造方法一起讲;

        

        super:Java中的关键字,可以理解为指向当前对象的超类(最近的一个超类)的指针,注意,this存储的是实际的地址(堆内存地址),super只是一个关键字,不存储任何地址

        super最常见的时候是使用在继承时,当子类继承父类时会自动继承父类的所有成员变量与方法,成员变量(静态+实例)可以直接通过super.xxx/子类对象.xxx调用。在通过子类对象调用父类中未重写的方法时,默认使用super.methodName()。注意:在子类继承的父类构造方法中,默认调用super(形参列表匹配)父类中的构造方法。

        super的用法(3种):

                1)直接引用:

                        可以通过super.xxx直接引用当前对象最近超类(直接继承类)的类的实例成员(变量/方法)。

public class Test {
    public static void main(String[] args) {
        Dog dog = new Dog();
        // 调用重写的run()方法
        dog.run();
        // 调用父类的run()方法
        dog.run_super();
    }
}

class Animal {
    // 实例变量age
    int age;
    // 实例方法run
    public void run(){
        System.out.println("Animal is running");
    }
}

class Dog extends Animal{
    public void run_super(){
        super.run();
    }

    @Override
    public void run() {
        System.out.println("Dog is running");
    }
}

                2)用于区分超类的方法与子类重写的同名方法:

                        例如:当子类没有重写父类中的run()方法时,子类对象.run()默认调用super.run(),即父类中的run()方法。如果子类重写了run()方法,子类对象.run()默认调用this.run()。例如下例中在run_all()

public class Test {
    public static void main(String[] args) {
        Dog dog = new Dog();
        // 调用run_all()方法
        dog.run_all();
    }
}

class Animal {
    // 实例变量age
    int age;
    // 实例方法run
    public void run(){
        System.out.println("Animal is running");
    }
}

class Dog extends Animal{
    public void run_all(){
        // 调用超类run()
        super.run();
        // 调用本类run()
        run();
    }

    @Override
    public void run() {
        System.out.println("Dog is running");
    }
}

                3)引用构造方法(超类)

                         super(参数):调用父类中的某一个构造函数(应该为构造函数中的第一条语句)。

                        this(参数):调用本类中另一种形式的构造函数(应该为构造函数中的第一条语句)。

class Animal {
    // 实例变量age
    int age;
    // 构造器
    public Animal() {}

    public Animal(int age) {
        this.age = age;
    }
}

class Dog extends Animal{
    String animalName;

    // 构造器
    public Dog() {
        this(0,null);
    }
    
    public Dog(int age) {
        this(age,null);
    }

    public Dog(int age, String animalName) {
        super(age);
        this.animalName = animalName;
    }
}

                注意:super()与this()必须出现在构造器的第一行,并且两者只能出现一个。

                注意在子类的构造器中调用父类的构造器并不会创建父类对象,对象是通过new关键字开辟堆内存创建的,构造器的主要作用是初始化实例变量。之所以在子类中调用父类的构造器是为了初始化继承自父类的实例变量。

        

        this与super的异同:

                1)super(args):调用最近超类中的同参构造器(应为构造器的第一条语句,否则编译不通过);

                2)this(args):调用本类中的同参构造器(应为构造器的第一条语句,否则编译不通过);

                3)super:引用当前对象的直接父类中的成员(当子类与超类类中有相同成员定义时通过super指明该成员是超类成员还是当前类成员。如:super.变量名 super.成员函数据名(实参)) ;this:它代表当前对象,是当前对象的引用(在本类中易产生二义性之处,应使用 this 来指明当前对象;如果函数的形参与类中的成员数据同名,这时需用 this 来指明成员变量名)

                4)在子类的构造器中,如果无参数,构造器第一行会隐式的通过调用 super()子句调用父类的无参构造器;如果父类里面没有定义参数为空的构造函数,那么必须在子类的构造函数的第一行显示的调用super(参数)以调用父类当中其它的构造函数;如果子类当中的构造函数的第一行通过this(参数)调用了本类的构造函数,那么就隐式的那个super()语句就会消失。

                5)this() 和 super() 都指的是对象,所以,均不可以在 static 环境中使用。包括:static 变量,static 方法,static 语句块。

                6)从本质上讲,this 是一个指向本对象的指针(存储对象在堆内存中的地址), 然而 super 是一个 Java 关键字(不存储地址)。

2. 可见性修饰符(访问权限):

        Java中为类、变量、方法的访问提供了4种访问修饰符:依照权限从大到小依次是:public、protected、default、private。其访问权限设置如下

        

        · pbulic:访问权限最广的修饰符,其修饰的类、变量、方法在内外包均可以访问;

        · protected:对于protected修饰的成员,对于本包和子类可见(注意:继承中的protected修饰符);

        · default:默认修饰符(未添加可见修饰符时默认设置),对于同包的类具有访问权限;

        · private:私有的权限,private修饰的成员(变量/方法)能被本类的成员访问;

        **注意:继承与访问控制符无关,子类会自动继承父类的所有属性与方法,访问控制符只是控制该变量/方法/类的访问权限,与继承无关。但父类中private/default修饰的变量/方法不允许不允许子类访问、调用或重写,其只允许父类的实例访问,或使用父类提供的接口访问,所以abstract类中的抽象方法不能用private修饰(无法重写)。

访问修饰符的使用实例:

        1)外部类/外部接口:(public/default):

                外部类/外部接口是相对于内部类/内部接口而言的,主要是为了保证所有的类都可以使用他们。

// 访问权限为public的外部类
public class OuterClass {

}

// 访问权限为public的外部接口
public interface OuterInterface {

}

        2)类成员:

                类成员可以使用所有的四种访问权限。

                *注意:

                        这里描述的类成员为成员变量与成员方法(均包括实例的与静态的),局部成员(局部变量与局部内部类)不需要任何修饰符,添加修饰符会导致编译无法通过。

                *数据域封装(重点):

                        数据域封装的含义是,对数据域(实例的属性:实例变量)使用 private 修饰符,将数据域声明为私有域。如果不使用数据域封装,则数据域的值可以从类的外部直接修改,导致数据被篡改以及类难以维护。使用数据域封装的目的是为了避免直接修改数据域的值。

                        同时,在定义私有数据域的类之外,不能通过直接引用的方式访问该私有数据域,但是仍然可能需要读取和修改数据域的值。为了能够读取私有数据域的值,可以编写 get 方法(称为读取器或访问器)返回数据域的值。为了能够修改私有数据域的值,可以编写 set 方法(称为设置器或修改器)将数据域的值设置为新值,例如:

// 银行账户类
public class Account {
    // 私有的账户id与用户名
    private int accountId;
    private String name;

    // 设置对外访问/修改的API
    // 获取账户id
    public int getAccountId() {
        return accountId;
    }
    // 修改账户id
    public void setAccountId(int accountId) {
        this.accountId = accountId;
    }
    // 获取用户名
    public String getName() {
        return name;
    }
    // 修改用户名
    public void setName(String name) {
        this.name = name;
    }
}

        3)抽象类(抽象方法3种):

        抽象类之所以为抽象类,是因为它是作为父类来使用的,是等待子类去实现的,而父类中 private的权限只能是由父类对象访问自个,子类是不能访问或重写的,所以在抽象类中方法为abstract时只有public,protected,default三种访问权限。(只要有一个抽象方法,该类即为抽象类;抽象类中并不是所有方法都是抽象方法)。

        4)接口成员(只有三种)的访问权限

                接口由于其的特殊性,所有成员的访问权限都规定得死死的(不可修改,可以少写修饰符,但不能写错修饰符),下面是接口成员的访问权限:

                · 常量:public static final;可以被接口/类继承

                · 抽象方法:public abstrct;可以被继承,但必须重写;

                · 静态方法:public static(JDK1.8以后支持)

                · 内部类/接口:public static;

interface Test{
    // 常量:省略了public static final
    int i = 0;
    // 抽象方法:省略了public abstract
    void print();
    // 静态方法:省略了public
    static void printing(){ }
    // 内部类:省略了public static
    class InnerTest{ }
    // 内部接口:省略了public static
    interface InnerTest_2{ }
}

        5)构造器:

                构造器可以使用4种访问权限,但子类的构造器默认调用父类的构造器初始化父类继承过来的对象,一旦调用失败,意味着子类继承失败(单例模式:private 构造器)

        其他修饰符:

                · static:声明其修饰的变量、方法、内部类/接口(不能修饰常规类/接口)是静态的,静态变量在类加载时创建并初始化,所有的静态/实例方法均可以调用;静态方法可以被静态/实例方法调用。注意:static修饰的代码块为静态代码块,在类加载时执行。

                · final:其声明的对象不可修改;final修饰的变量必须在声明时给定初值(空白final除外),其中通过public static final声明一个全局共享的基本类型常量,并存入常量池中;final修饰的方法不可重写,即不可被子类覆盖;final修饰的不可以被继承。

                · abstract:其声明的类、方法是抽象的;abstract声明的为抽象类,抽象类不能创建实例;abstract声明的方法为抽象方法,抽象方法没有方法体,子类继承父类/实现接口的抽象方法时需要重写该方法。

3. 字符串

        字符串是常用的数据类型。Java中常见的字符串类型包括String、StringBuffer、StringBuilder。

        1) String:

                虽然Java中的String是一个引用类型,但是从String的源码中可以看到String通过数组存储,并且该数组被关键字final修饰,说明String类型的对象一旦创建就要赋初始值,并且是不可修改的。事实上,String对象被存储于JVM的字符串常量池中,而非堆内存中,这意味着每次对String的赋值、修改操作都会在字符串常量池中创建一个新的对象。

        2)StringBuffer & StringBuilder:

                StringBuffer 与 StringBuilder 都是 AbstractStringBuilder 的子类,都采用数组存储字符串,但由于没有用关键字final修饰,因此数组内容可变。StringBuffer 和 StringBuilder 都是可变类型,可以对字符串的内容进行修改,且不会因为修改而创建新的对象。

                在需要经常对字符串的内容进行修改的情况下,应使用 StringBuffer 或 StringBuilder,在时间和空间方面都显著优于 String。

        *注意: StringBuffer 与 StringBuilder的区别

                从源码可以看到,StringBuffer 对定义的方法或者调用的方法使用了关键词 synchronized 修饰,而 StringBuilder 的方法没有使用关键词 synchronized 修饰。由于 StringBuffer 对方法加了同步锁,因此其效率略低于 StringBuilder,但是在多线程的环境下,StringBuilder 不能保证线程安全,因此 StringBuffer 是更优的选择。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: comparator是Java中的一个接口,用于比较两个对象的大小。它可以用于对集合中的元素进行排序,也可以用于自定义排序规则。实现comparator接口需要重写compare方法,该方法返回一个整数值,表示两个对象的大小关系。如果返回负数,则表示第一个对象小于第二个对象;如果返回正数,则表示第一个对象大于第二个对象;如果返回,则表示两个对象相等。comparator接口可以与Java中的排序算法一起使用,例如Collections.sort()方法。 ### 回答2: jmu-java-04面向对象进阶--02-接口-comparator讲述了Java中的接口以及比较器的使用。接口是一种约束,它规定了某个必须要实现哪些方法,但不需要具体的实现方式。比较器则是一种接口,它规定了两个对象之间的排序方式。 在Java中,接口的定义方式为interface,其中的方法默认为public abstract形式。定义接口,需要注意接口只能继承接口,并且可以有常量,但不能有成员变量。另外,接口中所有的方法都没有方法体,必须由实现它的去具体实现。举例来说,如果我们定义一个接口Animal,可以定义一个方法move(),而实现这个接口的必须实现move()方法,并且可以自由决定具体的实现方式,如Dog可以实现为跑步,Bird可以实现为飞行。 在讨论了接口的使用之后,jmu-java-04面向对象进阶--02-接口-comparator着重介绍了比较器的使用。比较器似于一个工具箱,可以定义多种比较方式供其他使用。比较器的核心是Comparator,其定义的方法为compare(),用于比较两个对象并返回结果(0、1或-1)。比较器可以用于对对象进行排序或查找指定的对象。 在使用比较器,需要实现Comparator接口,并覆盖compare()方法。比如,我们可以定义一个Person,并在其中实现Comparator接口,然后在compare()方法中指定按照年龄从小到大排序。当我们使用Collections.sort()对Person列表进行排序,就会按照我们定义的比较方式进行排序。 总的来说,jmu-java-04面向对象进阶--02-接口-comparator讲述了Java中的接口和比较器的使用,这是Java中优秀的编程方式之一,也是开发者必备的基本知识。掌握了接口和比较器的使用,我们就可以更好地实现面向对象编程,并对Java中的集合框架有更深刻的理解。 ### 回答3: Comparator是Java中一个非常重要的接口,它主要用于定义对象之间的比较规则。在Java中,比较规则是由比较器来实现的。比较器可以用于排序、查找和其他需要比较的场景。 Comparator接口有一个方法compare(Object o1, Object o2),用于比较两个对象的大小。如果o1大于o2,则该方法返回一个正整数;如果o1小于o2,则该方法返回一个负整数;如果o1等于o2,则该方法返回0。 我们可以使用Comparator接口来实现自定义的比较规则。比如,我们可以定义一个Student,包含姓名和年龄两个属性,然后实现一个比较器,按照年龄从小到大的顺序对Student对象进行排序。 可以通过使用Collections.sort()方法对Student对象进行排序,提供一个实现Comparator接口的比较器作为参数进行排序。 实现一个比较器还可以实现多种排序方式。例如,按照姓名从小到大排序,实现如下: ``` public class NameComparator implements Comparator<Student> { public int compare(Student s1, Student s2) { return s1.getName().compareTo(s2.getName()); } } ``` 在使用,我们可以将NameComparator对象作为参数传递给sort()方法,进行姓名排序。 Comparator接口的使用不仅仅局限于对象的比较排序,还可以用于其他需要比较的场景,比如查找、筛选等。例如,我们可以按照年龄筛选出年龄大于20岁的Student对象,并将它们存储在一个新的List中,实现如下: ``` List<Student> ageGreaterThan20 = students.stream() .filter(s -> s.getAge() > 20) .sorted(new AgeComparator()) .collect(Collectors.toList()); ``` 以上的代码使用了Java 8的新特性,使用流将年龄大于20岁的Student对象筛选出来,并按照年龄进行排序,最后存储在一个新的List中。 总之,Comparator是一个非常重要的接口,在Java中有着广泛的应用。掌握Comparator的使用可以帮助我们快速地实现对象比较、排序、筛选等操作,提高我们的编程效率。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值