---------------------------4.23-------------------------

---------------------------4.23-------------------------,第1张

1.面对对象基础。

面向对象编程,是一种通过对象的方式,把现实世界映射到计算机模型的一种编程方法 。

把抽象的概念具体的表达出来。//自己的理解

现实世界中,我们定义了“人”这种抽象概念,而具体的人则是“小明”、“小红”、“小军”等一个个具体的人。所以,“人”可以定义为一个类(class),而具体的人则是实例(instance):

现实世界计算机模型Java代码
类 / classclass Person { }
小明实例 / mingPerson ming = new Person()
小红实例 / hongPerson hong = new Person()
小军实例 / junPerson jun = new Person()

class是一种对象模版,它定义了如何创建实例,因此,class本身就是一种数据类型:

而instance是对象实例,instance是根据class创建的实例,可以创建多个instance,每个instance类型相同,但各自属性可能不相同: 

定义class

在Java中,创建一个类,例如,给这个类命名为Person,就是定义一个class(类)

class Person {
    public String name;
    public int age;
}

一个class可以包含多个字段(field),字段用来描述一个类的特征。上面的Person类,我们定义了两个字段,一个是String类型的字段,命名为name,一个是int类型的字段,命名为age。因此,通过class,把一组数据汇集到一个对象上,实现了数据封装。

public是用来修饰字段的,它表示这个字段可以被外部访问。

创建实例

定义了class,只是定义了对象模版,而要根据对象模版创建出真正的对象实例,必须用new *** 作符。

new *** 作符可以创建一个实例,然后,我们需要定义一个引用类型的变量来指向这个实例:

Person ming = new Person();

上述代码创建了一个Person类型的实例,并通过变量ming指向它。

有了指向这个实例的变量,我们就可以通过这个变量来 *** 作实例。例如:

ming.name = "Xiao Ming"; // 对字段name赋值
ming.age = 12; // 对字段age赋值
System.out.println(ming.name); // 访问字段name

Person hong = new Person();
hong.name = "Xiao Hong";
hong.age = 15;

上述两个变量分别指向两个不同的实例,它们在内存中的结构如下:

ming ──────>│Person instance   │
            ├──────────────────┤
            │name = "Xiao Ming"│
            │age = 12          │
            └──────────────────┘
            ┌──────────────────┐
hong ──────>│Person instance   │
            ├──────────────────┤
            │name = "Xiao Hong"│
            │age = 15          │
            └──────────────────┘

两个instance拥有class定义的nameage字段,且各自都有一份独立的数据,互不干扰。

 2.方法

一个class可以包含多个field,例如,我们给Person类就定义了两个field

class Person {
    public String name;
    public int age;
}

但是,直接把fieldpublic暴露给外部可能会破坏封装性。比如,代码可以这样写:

Person ming = new Person();
ming.name = "Xiao Ming";
ming.age = -99; // age设置为负数 

显然,直接 *** 作field,容易造成逻辑混乱。为了避免外部代码直接去访问field,我们可以用private修饰field,拒绝外部访问:

class Person {
    private String name;
    private int age;
}

fieldpublic改成private,外部代码不能访问这些field,所以我们需要使用方法(method)来让外部代码可以间接修改field

public class Main {
    public static void main(String[] args) {
        Person ming = new Person();
        ming.setName("Xiao Ming"); // 设置name
        ming.setAge(12); // 设置age
        System.out.println(ming.getName() + ", " + ming.getAge());
    }
}

class Person {
    private String name;
    private int age;

    public String getName() {
        return this.name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return this.age;
    }

    public void setAge(int age) {
        if (age < 0 || age > 100) {
            Sytem.out.println("invalid age value");
        }
        this.age = age;
    }
}

外部代码可以调用对象内方法间接修改private字段,同时让对象内部保持逻辑一致性。

调用方法的语法是实例变量.方法名(参数);。一个方法调用就是一个语句,所以不要忘了在末尾加;。例如:ming.setName("Xiao Ming");。 

定义方法

从上面的代码可以看出,定义方法的语法是:

修饰符 方法返回类型 方法名(方法参数列表) {
    若干方法语句;
    return 方法返回值;
}
private方法

public方法,自然就有private方法。和private字段一样,private方法不允许外部调用,那我们定义private方法有什么用?

定义private方法的理由是内部方法是可以调用private方法的。

public class Main {
    public static void main(String[] args) {
        Person ming = new Person();
        ming.setBirth(2008);
        System.out.println(ming.getAge());
    }
}

class Person {
    private String name;
    private int birth;

    public void setBirth(int birth) {
        this.birth = birth;
    }

    public int getAge() {
        return calcAge(2019); // 调用private方法
    }

    // private方法:
    private int calcAge(int currentYear) {
        return currentYear - this.birth;
    }
}

观察上述代码,calcAge()是一个private方法,外部代码无法调用,但是,内部方法getAge()可以调用它。

此外,我们还注意到,这个Person类只定义了birth字段,没有定义age字段,获取age时,通过方法getAge()返回的是一个实时计算的值,并非存储在某个字段的值。这说明方法可以封装一个类的对外接口,调用方不需要知道也不关心Person实例在内部到底有没有age字段。

 类通常对客户隐藏其细节,这就是封装的思想。

this变量

在方法内部,可以使用一个隐含的变量this,它始终指向当前实例(当前对象)。因此,通过this.field就可以访问当前实例的字段。

如果没有命名冲突,可以省略this。例如:

class Person {
    private String name;

    public String getName() {
        return name; // 相当于this.name
    }
}

但是,如果有局部变量和字段重名,那么局部变量优先级更高,就必须加上this: 

class Person {
    private String name;

    public void setName(String name) {
        this.name = name; // 前面的this不可少,少了就变成局部变量name了
    }
}

参数绑定

调用方把参数传递给实例方法时,调用时传递的值会按参数位置一一绑定。

那什么是参数绑定?

我们先观察一个基本类型参数的传递:

public class Main {
    public static void main(String[] args) {
        Person p = new Person();
        int n = 15; // n的值为15
        p.setAge(n); // 传入n的值
        System.out.println(p.getAge()); // 15
        n = 20; // n的值改为20
        System.out.println(p.getAge()); // 15还是20?
    }
}

class Person {
    private int age;

    public int getAge() {
        return this.age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

运行代码,从结果可知,修改外部的局部变量n,不影响实例page字段,原因是setAge()方法获得的参数,复制了n的值,因此,p.age和局部变量n互不影响。

结论:基本类型参数的传递,是调用方值的复制。双方各自的后续修改,互不影响。

我们再看一个传递引用参数的例子:

public class Main {
    public static void main(String[] args) {
        Person p = new Person();
        String[] fullname = new String[] { "Homer", "Simpson" };
        p.setName(fullname); // 传入fullname数组
        System.out.println(p.getName()); // "Homer Simpson"
        fullname[0] = "Bart"; // fullname数组的第一个元素修改为"Bart"
        System.out.println(p.getName()); // "Homer Simpson"还是"Bart Simpson"?
    }
}

class Person {
    private String[] name;

    public String getName() {
        return this.name[0] + " " + this.name[1];
    }

    public void setName(String[] name) {
        this.name = name;
    }
}

注意到setName()的参数现在是一个数组。一开始,把fullname数组传进去,然后,修改fullname数组的内容,结果发现,实例p的字段p.name也被修改了!

结论:引用类型参数的传递,调用方的变量,和接收方的参数变量,指向的是同一个对象。双方任意一方对这个对象的修改,都会影响对方(因为指向同一个对象嘛)。

 引用类型参数和基本类型参数的解释:

基本类型参数和引用类型参数_呜啦啦、的博客-CSDN博客_引用类型参数

 3.构造方法

 创建实例的时候,我们经常需要同时初始化这个实例的字段,我们完全可以在创建对象实例时就把内部字段全部初始化为合适的值。

这时,我们就需要构造方法。

创建实例的时候,实际上是通过构造方法来初始化实例的。我们先来定义一个构造方法,能在创建Person实例的时候,一次性传入nameage,完成初始化:

public class Main {
    public static void main(String[] args) {
        Person p = new Person("Xiao Ming", 15);
        System.out.println(p.getName());
        System.out.println(p.getAge());
    }
}

class Person {
    private String name;
    private int age;

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

    public int getAge() {
        return this.age;
    }
}

由于构造方法是如此特殊,所以构造方法的名称就是类名。构造方法的参数没有限制,在方法内部,也可以编写任意语句。但是,和普通方法相比,构造方法没有返回值(也没有void),调用构造方法,必须用new *** 作符。

默认构造方法

是不是任何class都有构造方法?是的。

那前面我们并没有为Person类编写构造方法,为什么可以调用new Person()

原因是如果一个类没有定义构造方法,编译器会自动为我们生成一个默认构造方法,它没有参数,也没有执行语句,类似这样:

 class Person{

                public Person(){

        }

}

 要特别注意的是,如果我们自定义了一个构造方法,那么,编译器就不再自动创建默认构造方法:

如果既要能使用带参数的构造方法,又想保留不带参数的构造方法,那么只能把两个构造方法都定义出来:

同c语言一样当我们创建对象时可以直接对字段直接进行初始化(对属性赋值),如果既对字段进行初始化,又在构造方法中对字段进行初始化,那么最终结果由构造方法的代码确定。

在Java中,创建对象实例的时候,按照如下顺序进行初始化:

  1. 先初始化字段,例如,int age = 10;表示字段初始化为10double salary;表示字段默认初始化为0String name;表示引用类型字段默认初始化为null

  2. 执行构造方法的代码进行初始化。

多构造方法

可以定义多个构造方法,在通过new *** 作符调用的时候,编译器通过构造方法的参数数量、位置和类型自动区分:

一个构造方法可以调用其他构造方法,这样做的目的是便于代码复用。调用其他构造方法的语法是this(…): 

class Person {
    private String name;
    private int age;

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

    public Person(String name) {
        this(name, 18); // 调用另一个构造方法Person(String, int)
    }

    public Person() {
        this("Unnamed"); // 调用另一个构造方法Person(String)
    }
}

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/723547.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-04-26
下一篇 2022-04-26

发表评论

登录后才能评论

评论列表(0条)

保存