欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Java面向对象[类和对象]

程序员文章站 2022-05-25 20:49:15
...
  • 面向对象的三大特征:封装、继承和多态,java提供了private、protected和public三个访问控制修饰符来实现良好的封装
  • 提供了extends关键字来让子类继承父类,子类继承父类就可以继承到父类中的成员变量和方法,如果访问控制允许,那么子类实例可以直接调用父类定义的方法
  • 继承是实现类复用的重要手段,除此之外也可以使用组合关系来实现复用,从某个角度说继承和组合具有相同的功能,使用继承关系来实现复用时,子类对象可以直接赋给父类对象变量,这个变量具有多态性,编程更灵活,而组合关系实现复用时并不具备这个优势

定义类、成员变量和方法

【这句看着晕就忽略】Java是以对象为中心的,最小的程序单位就是类,整个java程序由一个一个类组成,可以把类当成一种自定义的引用类型,可以使用类来定义变量,这种类型的变量统称为引用变量。

定义类

面向对象的程序中有两个比较重要的概念一个是【类】,另一个是【对象】或者叫做【类的实例】,非常形象的举个例子:地球上的人属于【人】类,人类可以有两个子类【男/女】,其中【男】类的对象或者具体的实例可以是【张三】

[修饰符] class 类名                        //[修饰符]可以是public、final、abstract
{
    零个到多个构造器定义...
    零个到多个成员变量...
    零个到多个方法...
}
  • 注意 1:类名从程序的可读性方面来看,java类名必须是由一个或者多个意义明确的词或者词的前缀组成,每个字首字母大写,其他字母全部小写,单词与单词之间不要用任何分隔符。
  • 注意2:类里的成员,static修饰的成员不能访问没有static修饰的成员
  • 注意3:类里的成员顺序没有任何影响,各成员之间可以相互调用,【成员变量】用于定义该类或者该类的实例所包含的状态数据,【方法】用于定义该类或者该类的实例的行为特征或者功能实现,【构造器】用于构造该类的实例,java通过关键字new来调用构造器创建该类的实例。
  • 注意4:【构造器】是类创建对象的根本途径,如果一个类没有构造器,那么通常它不能被创建实例,因此java提供了默认构造器,如果程序员 写了构造器,那么java就不再为这个类提供默认构造器

定义成员变量

[修饰符]类型 成员变量[=默认值];

  • 修饰符:可以省略,也可以是public、protected、private、static、final,并且public、protected和private三个修饰符只能出现其中一个,不能同时修饰同一个成员变量,它们可以和static和final组合使用。
  • 类型:是java语言所允许的任何数据类型,包括基本类型、引用类型
  • 成员变量名:从程序可读性角度来规范,它应该是由一个或者多个有意义的单词连缀而成,第一个单词的首字母小写,后面每个单词的首字母大写,其他字母全部小写,单词与单词之间不要使用任何分隔符
  • 默认值:定义成员变量还可以指定一个可选的默认值
    注意:成员变量来自于英文field,并不能称为属性,在java世界里属性来自于单词property指的是一组setter方法和getter方法,比如说类有age属性,那么意味着该类包含setAge()和getAge()两个方法。

定义方法

[修饰符]方法返回值类型 方法名(形参列表)
{
    //由零条或者多条执行语句组成的方法体
}
  • 修饰符:修饰符可以省略,也可以是public、protected、private、static、final和abstract,其中public、protected和private只能出现其一,final和abstract只能出现其一,它们可以和static组合起来使用
  • 方法返回值类型:返回值类型可以是java允许的任何数据类型,包括基本类型和引用类型;如果声明了方法返回值则方法体内必须有一个有效的return语句,该语句返回一个变量或者一个表达式,这个变量或者表达式必须与声明的方法返回值的数据类型一致;如果一个方法没有返回值,则此处必须使用void来声明没有返回值
  • 方法名:命名规则与成员变量的命名规则基本一致,但由于方法是描述类的实例的行为特征或者功能实现的,因此方法名最好以英文动词开头
  • 形参列表:形参列表定义了该方法所能接受的参数,由零组或者多组“参数类型 形参名”组合而成,多组参数之间用英文逗号隔开,形参类型和形参名之间用英文空格隔开
  • 方法调用:定义方法时指定了形参列表,则调用该方法时必须传入对应的参数值,谁调用方法谁负责为形参赋值
  • 方法体内语句是顺序结构,从上至下依次执行
  • static是个特殊的关键字,它可以修饰方法、成员变量等成员,static修饰成员则表明它属于类本身,而不属于该类的单个实例,通常用static修饰的方法或者成员变量被称为类方法、类成员变量;不适用static修饰的方法或者成员变量属于该类的单个实例,通常称为实例方法、实例变量;也可以成为【类方法&类成员变量=静态方法&静态变量】【实例方法&实例变量=非静态方法&非静态变量】

构造器

构造器是一个特殊的方法,定义构造器的语法格式与定义方法的语法格式很像

[修饰符]构造器名(形参列表)
{
    //由零调到多条可执行语句组成的构造器执行体
}
  • 修饰符:修饰符可以省略,也可以是public、protected或者private其中之一
  • 构造器名:必须与类名相同
  • 形参列表:与定义方法形参列表的格式完全相同,但系统提供的构造器是没有参数的
  • 构造器不能定义返回值类型,也不可使用void来表明构造器没有返回值,否则编译的时候虽然不会报错,但java就会把构造器当成方法来处理
  • 构造器是有返回值的,它返回的就是当我们用new关键字创建实例时,调用了类的构造器,构造器负责隐式的返回实例从而我们可以得到一个类的实例,并且构造器中不需要也不能使用return来返回

创建并使用对象

public class Person
{
    // 下面定义了两个成员变量
    public String name;
    public int age;
    // 下面定义了一个say方法
    public void say(String content)
    {
        System.out.println(content);
    }
}
public class PersonTest
{
    public static void main(String[] args)
    {
        // 使用Peron类定义一个Person类型的变量
        Person p;
        // 通过new关键字调用Person类的构造器,返回一个Person实例,
        // 将该Person实例赋给p变量。
        p = new Person();
        // Person p = new Person();

        // 访问p的name实例变量,直接为该变量赋值。
        p.name = "davieyang";
        // 调用p的say方法,声明say()方法时定义了一个形参,
        // 调用该方法必须为形参指定一个值
        p.say("Java语言很简单,学习很容易!");
        // 直接输出p的name实例变量,将输出 davieyang
        System.out.println(p.name);

        // 将p变量的值赋值给p2变量
        var p2 = p;
    }
}
  • 关于调用:如果访问权限允许,类里定义的方法和成员变量都可以通过类或者实例来调用【类.类变量|方法】【实例.实例变量|方法】,static修饰的方法和成员变量,既可以通过类来调用,也可以通过实例来调用;没有static修饰的普通方法和成员变量只能通过实例来调用
  • 关于类:定义类最大的目的就是为了重复创建该类的实例,同一个类的多个实例具有相同的特征,而类则是定义了多个实例的共同特征,类并不是一种具体的存在,实例才是具体的存在

对象和引用

与数组类型类似,类也是一种引用数据类型,因此程序中定义的Person类型的变量实际上是一个引用,它被存放在栈内存中,并指向实际的Person对象,而真正的Person对象存放在堆内存中,P只是存储了一个地址值不包含任何实际数据。
Java面向对象[类和对象]

// 将p变量的值赋值给p2变量
var p2 = p;
  • 堆内存中的对象可以有多个引用,即多个引用变量指向同一个对象,将p给了p2实际上就是将p中存储的地址值复制一份给p2,自此p2和p引用相同的对象,无论是用p调用还是p2调用,都会返回相同的结果
  • 与数组相同,如果堆内存中的对象没有任何引用,那么它将不能被访问到,成为垃圾,Java的回收机制将回收它并释放内存
  • 如果希望对象被垃圾回收器回收,只需要切断引用,直接将引用变量赋值为null即可

对象的THIS引用

this关键字总是指向调用该方法的对象,作为对象的的默认引用有两种形式:

  • 构造器中引用该构造器正在初始化的对象
  • 在方法中引用调用该方法的对象
    this关键字最大的作用就是让类中的一个方法可以访问该类里的另一个方法或者实例变量
public class Dog
{
    // 定义一个jump()方法
    public void jump()
    {
        System.out.println("正在执行jump方法");
    }
    // 定义一个run()方法,run()方法需要借助jump()方法
    public void run()
    {
//        var d = new Dog();
//        d.jump();
        // 使用this引用调用run()方法的对象
        this.jump();
        System.out.println("正在执行run方法");
    }
}
public class DogTest
{
    public static void main(String[] args)
    {
        // 创建Dog对象
        var dog = new Dog();
        // 调用Dog对象的run()方法
        dog.run();
    }
}
  • this可以代表任何对象,当this出现在某个方法中时,它所代表的对象是不确定的,但它的类型是确定,它所代表的只能是当前类的实例,只有当这个方法被调用时,它所代表的对象才被确定下来,谁在调用这个方法this就代表谁
  • 大部分时候,一个方法访问该类中定义的其他方法、成员变量时加不加this前缀效果完全一样
  • 对于static修饰的方法而言,可以使用类来直接调用该方法,如果在static修饰的方法中使用了this,则这个关键字无法指向合适的对象,因此static修饰的方法中不能使用this关键字
  • 省略this关键字只是个假象,一般来说,如果调用static修饰的成员(方法&成员变量)时省略了前面的主调,那么默认使用该类作为主调,如果调用没有static修饰的成员(方法&成员变量)时省略了前面的主调,那么默认使用this作为主调

异常情况

public class StaticAccessNonStatic
{
    public void info()
    {
        System.out.println("简单的info方法");
    }
    public static void main(String[] args)
    {
        // 因为main()方法是静态方法,而info()是非静态方法,
        // 调用main()方法的是该类本身,而不是该类的实例,
        // 因此省略的this无法指向有效的对象
        info();
    }
}

编译这段代码会报错无法从静态上下文中引用非静态方法infor()
info()属于实例方法,而不是类方法,因此必须使用对象来调用该方法,在main()方法中直接调用info()方法时,系统相当于使用this作为该方法的调用者,然而main()方法是个static修饰的方法,static修饰的方法属于类而不是对象,因此调用static修饰的方法的主调总是类本身。
java编程时不要使用对象去调用static修饰的成员变量、方法,而是应该是用类去调用static修饰的成员变量、方法
修改代码如下:

public class StaticAccessNonStatic
{
    public void info()
    {
        System.out.println("简单的info方法");
    }
    public static void main(String[] args)
    {
        // 创建一个对象作为调用者来调用info()方法
        new StaticAccessNonStatic().info();
    }
}

虽然大部分时候,普通方法访问其他方法、成员变量时无需使用this前缀,但如果方法里有个局部变量和成员变量同名,但程序又需要在该方法里访问这个被覆盖的成员变量,则必须使用this前缀。

this引用也可以用于构造器中作为默认引用,由于构造器是直接使用new关键字来调用,而不是使用对象来调用,所以this在构造器中代表该构造器正在初始化的对象

public class ThisInConstructor
{
    // 定义一个名为foo的成员变量
    public int foo;
    public ThisInConstructor()
    {
        // 在构造器里定义一个foo变量
        int foo = 0;
        // 使用this代表该构造器正在初始化的对象
        // 下面的代码将会把该构造器正在初始化的对象的foo成员变量设为6。
        this.foo = 6;
    }
    public static void main(String[] args)
    {
        // 所有使用ThisInConstructor创建的对象的foo成员变量
        // 都将被设为6,所以下面代码将输出6。
        System.out.println(new ThisInConstructor().foo);
    }
}

当this作为对象的默认引用使用时,程序可以像访问普通引用变量一样来访问这个this引用,甚至可以把this当成普通方法的返回值。

public class ReturnThis
{
    public int age;
    public ReturnThis grow()
    {
        age++;
        // return this返回调用该方法的对象
        return this;
    }
    public static void main(String[] args)
    {
        var rt = new ReturnThis();
        // 可以连续调用同一个方法
        rt.grow()
            .grow()
            .grow();
        System.out.println("rt的age成员变量值是:" + rt.age);
    }
}

如果在某个方法中把this作为返回值,则可以多次连续调用同一个方法,使得代码更简洁,但也会造成实际意义的模糊。