目录

一、类和对象

1、什么是类和对象

2、类和对象的关系

二、类的定义

1、成员变量

2、成员方法

3、构造方法

三、如何创建对象

1、创建对象及其初始化

2、方法调用

 3、this关键字

4、static关键字

四、封装、继承和多态

1、类与其成员的访问修饰符和其他修饰符

2、封装

3、继承的实现

4、多态——覆盖与重载


        引言:本文主要详细解释了Java中如何正确的创建和使用类和对象,及其之间的关系,希望能对各位起到一定的帮助。

一、类和对象

1、什么是类和对象

        在程序员的眼中,“万物皆对象”,走在大街上,我们目之所及都是对象。比如路上的散步的小情侣,重庆的黄色法拉利、花、草、树木等等之类都是对象,因为他们都有自己的属性和行为。举个例子,一辆汽车的属性有:品牌、型号、颜色、出厂日期、使用年限、出厂价格等;一个人的属性有:姓名、性别、年龄、身份证号、家庭住址、籍贯、电话号码等。由此可以发现对象就是真实世界的一个个实体,现实世界的每一个实体都是一个对象,都有与之对应的属性。在JAVA中的对象也是如此,它将现实中的对象用代码抽象表示出来,一个对象代表某一具体操作,多个对象组成一个完整的应用程序。对象可以是独立存在,也可以是从其它对象继承而来,通过对象之间的互相传递信息,实现完整的程序开发,好比现实中万事万物之间的联系,构成这个五彩缤纷的世界。

        类是描述一组具有相同特征(属性)和相同行为(方法)的对象的集合。对象的特征在类中统称为类的属性。把所有人看作一个集合,也就是一个类,当中的每个人都有姓名、性别和年龄,但是每个人的姓名、性别和年龄又各不相同。例如,张三和李四都有各自的姓名,性别和年龄,这是他们作为一个人的特征,也就是属性,但是他们的姓名,性别和年龄又都不相同。同一类中对象执行的操作叫做类的方法,比如:人都会吃饭睡觉,张三在吃饭,李四在睡觉,这里张三和李四作为人这个类中的对象,张三执行的是吃饭这个方法,而李四执行的是睡觉这个方法。

2、类和对象的关系

        第一段中讲了,类是具有相同特征和行为的集合,而对象就是这个集合中的一个个实体。所以的人是一个类,我们都是这个类中的一个个具体的对象,我们共同的特征有都有姓名、性别、身份证号......,共同的行为是我们都会吃饭、睡觉、喝水......。类就是一个模板,通常是不具体的,但是类的对象是具体的,因此我们可以说对象是类的实例。

        程序世界中类就是一个基本单位,是构成完整应用程序的一个个集合,类的对象就是集合中的一个个实例,对象通过执行各种方法来完成应用的各种操作。在开发设计时,程序员需要首先创建不同的类,然后再创建类的对象,通过对象来执行类的方法。

 

二、类的定义

1、成员变量

        类的变量也就是类的属性,根据定义位置的区别,区分为两大类:成员变量(储存在堆内存中,和类一起创建)和局部变量(储存在栈内存中)。二者的运行机制有很大区别。

        成员变量就是在定义在类中各个方法体之外的变量,局部变量就是类中定义在各个方法体之中的变量。成员变量可以被类中的方法、构造方法和特点类访问和修改。而局部变量只在所在方法体类有效,所在方法体之外的其余方法和构造方法都无法访问。

public class Score(){ 
    public String name;//类score的成员变量
    public String sex;//类score的成员变量
    public int results;//类score的成员变量
}
public static void main(String[]agrs){
    Score p1=new Score();//创建类Score的对象p1
    Score p2=new Score();//创建类Score的对象p2
    p1.name="张三";//成员变量赋初值
    p2.name="李四";
    p1.sex="男";
    p1.sex="男";
    p1.results=90;
    p2.results=80;
    System.out.println("p1的姓名:"+p1.name+",性别:"+p1.sex+",成绩"+p1.results);
    System.out.println("p2的姓名:"+p2.name+",性别:"+p2.sex+",成绩"+p2.results);
}

        以上代码输出结果为:

                        p1的姓名:张三,性别:男,成绩:90

                        p2的姓名:李四,性别:男,成绩:80

        当程序第一次执行Score p1=new Score();这个语句时,程序会加载这个类,并初始化,为其中的成员变量分配内存空间,并指定默认值。

        当程序执行p1.name="张三";语句时,Score类的对象p1的成员变量name就被赋值为张三,执行其余语句,p1和p2的成员变量都被赋值。

2、成员方法

        在JAVA中,方法只能作为类的成员,称其为成员方法,成员方法是描述对象具有的功能和行为,是相对独立的程序模块。其作用主要是操作类的成员变量,一般情况下,程序主要是通过类的成员方法来实现交互。

        成员方法根据有无参数和返回值分为四种:带参无返回值的方法、带参有返回值的方法、无参无返回值的方法、无参有返回值的方法。

public class Main {
    public static void main(String[] args) {
        Score p1=new Score();
        p1.A();
        System.out.println(p1.B());
        p1.C("张三","男",20);
        System.out.println(p1.D("张三","男",20));
    }
}
class  Score{
    public String name;
    public String sex;
    public int age;
    //无参数无返回值
    public void A(){
        System.out.println("无参无返回值");
    }
    //无参数有返回值,这里返回值类型为String类型
    public String B(){
        return "无参有返回值";
    }
    //带参无返回值
    public void C(String name,String sex,int age){
        System.out.println("带参无返回值");
    }
    //带参有返回值,这里返回值为String类型
    public String D(String name,String sex,int age){
        return "带参有返回值";
    }
}

        输出结果为:

                        无参无返回值
                        无参有返回值
                        带参无返回值
                        带参有返回值

3、构造方法

        在JAVA中,对象的成员在被使用前都需要赋初值,JAVA提供了为类的成员变量赋初值的专门方法——构造方法。

        构造方法是特殊的方法,它与类同名,没有返回值,也不需要void的方法。对象的创建都是通过构造方法来完成,当类创建对象时会自动调用构造方法,此时构造方法会为对类所创建的对象的成员变量初始化。例:

        Score p1=new Score();

        这里的Score()就是构造方法,没有参数;

        如果类中没有定义任何构造方法,那么系统会提供一个不带任何参数的构造方法,被称为默认构造方法。构造方法就是我们创建对象时系统自动调用用来给对象的成员变量初始化的,程序员无法直接调用。子类继承父类时也默认继承父类的构造方法,即子类存在隐含方法super(),如果子类重写构造方法则子类也隐含调用super()。

        构造方法分两种:无参构造方法和带参构造方法。同时构造方法可以被重载。

public class Main {
    public static void main(String[] args) {
        Person p1=new Person();//这里调用的是无参构造方法
        Person p2=new Person("李四","500230199912031202",23);//这里调用的是带参构造方法
        System.out.println("p1的姓名:"+p1.name+",p1的身份证号:"+p1.id+"p1的年龄:"+p1.age);
        System.out.println("p2的姓名:"+p2.name+",p2的身份证号:"+p2.id+"p2的年龄:"+p2.age);
    }
}
class Person{
    public String name;
    public String id;
    public int age;
    //无参构造方法
    public Person(){
        name="张三";
        id="500240200003221313";
        age=22;
    }
    //带参构造方法
    public Person(String name,String id,int age){
        this.name=name;
        this.id=id;
        this.age=age;
    }
}

输出结果为:

                p1的姓名:张三,p1的身份证号:500240200003221313p1的年龄:22
                p2的姓名:李四,p2的身份证号:500230199912031202p2的年龄:23

可以看到,p1和p2因为调用了不同的构造方法导致其属性不同,输出结果也不同。p1调用的是无参构造方法,该构造方法对其成员变量进行了初始化;p2调用的是带参的构造方法,这里将实际参数赋值给了p2的成员变量从而完成初始化。

三、如何创建对象

1、创建对象及其初始化

        在JAVA中,对象在可以被使用前必须要被正确的初始化,这是JAVA所规范的。在实例化一个对象时,JAM首先会检查相关类型是否已经加载并初始化,如果没有,JVM会立即加载并调用类的构造方法对其初始化。在类初始化过程中或初始化完毕后,根据具体情况才会对类实例化。JAVA中最常见的就是使用new关键字来调用类的构造函数从来创建对象。例:

        Person p1=new Person();

        也可以通过这种方法调用带参的构造方法创建对象,代码如下:

        Person p2=new Person(“张三”,“男”,22);

        每一个类所创建的对象都是独立存在的,都独立存储自己的成员变量,互不影响。举一个通俗易懂的例子:张三和李四都是人这个类这的对象,他们都拥有姓名、性别、身份证号这些成员变量,张三的姓名不会因为李四而改变,李四的性别也不会因为张三而改变,他们的这些具体特征互不影响,两者之间没有任何联系。

2、方法调用

        一个类的方法被创建之后,需要对其进行调用,这也是方法存在的意义。通常有两种调用方式。

      (1)、单独调用

                对象名称.方法名称(实际参数);例:

                p1.print();

                p2.print(13,14);

                在上面两条语句中,第一条语句调用的是无参方法,第二种调用的是带参方法。如果需要将其输出,加上一个System.out.printin();即可。例:

        System.out.println(p1.print());
        System.out.println(p2.print(13,14));

        (2)赋值调用 

                数据类型  变量名称=对象名称.方法名称(实际参数);或      变量名称=对象名称.方法名称(实际参数);               

public class Main {
    public static void main(String[] args) {
        A p=new A();
        int s=p.S(2,3);//赋值调用
        System.out.println("长为2宽为3的矩形面积为"+s);
    }
}
class A{
    public int S(int a,int b){
        System.out.println("此方法计算矩形的面积");
        return a*b;
    }
}

输出结果:        

                        此方法计算矩形的面积
                        长为2宽为3的矩形面积为6               

在上面代码中,我们创建了类A,类A中的方法S用来计算矩形的面积,代码执行过程:

1、调用方法S,同时传递参数2,3

2、找到并执行方法S的方法体,同时返回值到调用方法处

3、将返回值赋值给s

需要注意的是,赋值调用的方法必须是拥有返回值的方法,如果调用无返回值的方法赋值给变量,程序会报错!

 3、this关键字

        this可用于任何实例方法中,可指向当前对象,也可对其调用当前方法的对象。此外,当类的成员变量与访问该属性的方法的参数名相同时,需要用this关键字来区分类的成员变量和方法的参数。例:

public class Main {
    public static void main(String[] args) {
        A p=new A();
        p.S(1,2);//调用方法S来访问修改类A的成员变量
        System.out.println(p.a);
        System.out.println(p.b);
    }
}
class A{
    public int a;
    public int b;
    public void S(int a,int b){
        this.a=a;//this用来区分类的成员变量a和方法S的参数a,下同
        this.b=b;
    }
}

输出结果为:

                                1

                                2

        这里的this关键字作用在于区分类A的成员变量a,b和方法S的参数a,b。

4、static关键字

        static主要用于内存管理。主要在成员变量、成员方法、块和内部类中使用。stitic关键字属于类,但不是类的实例。

        (1)、修饰成员变量

                static最常用的就是修饰类的成员变量和成员方法,让他们成为类的成员变量和成员方法,一般将用static修饰的成员成为类成员或静态成员。非静态的对象的成员变量和方法叫实例成员或非静态成员。

                例:

public class Main {
    public static void main(String[] args) {
        Score p1=new Score();
        Score p2=new Score();
        p1.name="张三";
        p2.name="李四";
        p1.sex="男";
        p2.sex="男";
        p1.age=21;
        p2.age=30;
        System.out.println("p1的姓名:"+p1.name+",性别:"+p1.sex+",年龄:"+p1.age);
        System.out.println("p2的姓名:"+p2.name+",性别:"+p2.sex+",年龄:"+p2.age);
    }
}
class  Score{
    public String name;
    public String sex;
    public static int age;//这里的成员变量age使用了static修饰,注意观察输出结果
}

输出结果为:

                        p1的姓名:张三,性别:男,年龄:30
                        p2的姓名:李四,性别:男,年龄:30

                在上述代码中,将成员变量age用static修饰以后,结果发生了一些变化,可以发现,我们为p1的age属性赋值为21,但是输出结果为30!这是因为在给age属性添加static关键字后,类Score的对象p1,p2就不在拥有age属性,此时的age属性属于类Score,由类去管理,此时即使不同对象都对age属性做了改变,age属性的值只会为最后一次被修改后的值。

        (2)、修饰成员方法

                static的另一个方法就是修饰成员方法,static修饰成员方法时不会对数据的存储产生影响,最大的作用就是可以直接使用“类名+方法名”的方式来调用方法,省略了创建对象这一操作,可以有效避免创建对象的繁琐和资源消耗。例:

public class Main {
    public static void main(String[] args) {
        A.Print();//直接使用类名+方法名调用方法
    }
}
class A{
    public static void Print(){
        System.out.println("Hello World!");
    }
}

输出结果为:

                                Hello World! 

                但是需要注意的是:使用static修饰的方法中不能使用非static修饰的成员变量,也不能调用非static修饰的其他方法,因为此时被static修饰的方法属于类,如果直接使用对象的成员变量,它无法准确判断应该使用哪一个对象的属性。与之相反的是非static修饰的方法可以调用static修饰的成员变量。

四、封装、继承和多态

1、类与其成员的访问修饰符和其他修饰符

        JAVA的访问修饰符也叫访问控制符,是能够控制类、成员变量、方法的使用权限的关键字。访问修饰符有以下四种:

        public(公共的):访问权限最低,所以类都可以访问

        default(缺省的):在当前包内可以访问

        protected:当前类和它的子类可以访问

        private:访问权限最高,仅当前类可以访问

2、封装

        封装就是将代码的过程和数据隐藏起来避免外界直接访问,优点是可以隐藏私有数据,让使用者只能通过公用的方法来访问这些数据,大大的便利的数据的检查,有利于保护对象信息的完整性。此外还便于修改代码,提高维护性。JAVA中通过关键字private来实现封装。通过降低代码的访问权限来保护代码的完整性。

public class Main {
    public static void main(String[] args) {
        Person p1=new Person();
        Person p2=new Person();
        p1.SetNumber("2021113029");
        p2.SetNumber("200003333");
        p1.SetName("万里");
        p2.SetName("阿剑");
        p1.SetAge(25);
        p2.SetAge(22);
        System.out.println("赋值后,对象p1的学号:"+p1.GetNumber()+",姓名:"+p1.GetName()+",年龄:"+ p1.GetAge());
        System.out.println("赋值后,对象p2的学号:"+p2.GetNumber()+",姓名:"+p2.GetName()+",年龄:"+ p2.GetAge());
    }
}
class Person{
    private String number;
    private String name;
    private int age;
    public Person(){
        number="2018113029";
        name="山河";
        age=22;
    }

    public void SetNumber(String number) {
        this.number = number;
    }
    public String GetNumber(){
        return number;
    }
    public void SetName(String name){
        this.name=name;
    }
    public String GetName(){
        return name;
    }
    public void SetAge(int age){
        this.age=age;
    }
    public int GetAge(){
        return age;
    }
}

输出结果为

                        赋值后,对象p1的学号:2021113029,姓名:万里,年龄:25
                        赋值后,对象p2的学号:200003333,姓名:阿剑,年龄:22

        上述代码中,在定义类Person时将它的成员变量number,name,age设置成了私有变量,这样其它类对象就不能直接访问这些成员变量,只能通过提供的公用的Set和Get方法来对其进行访问和修改,确保了成员变量的准确性和完整性。

3、继承的实现

        继承就是子类继承父类的特征和行为,使子类对象同时具有父类对象的特征,同时子类继承父类的方法,在子类中无需定义即可调用父类中存在的方法。继承的意义在于,大大提高了需要重复使用旧代码的效率,能够极大缩短开发周期,降低开发费用。继承是除组合外提高代码重复使用效率的主要方法。

        继承的主要作用在于在已有的基础上继续进行功能的扩充,使用extends关键字来实现。

public class Main {
    public static void main(String[] args) {
        Person p=new Person("阿剑","男",23);//创建对象p,同时利用提供的带参构造方法初始化类Person的成员变量
        p.print();//调用自定义方法print输出结果
        System.out.println("-------------分割线-------------");
        Employee p1=new Employee("山河","男",23,"中国人民解放军陆军","新疆喀什");
        p1.print1();
    }

public class Person {//在同一模块内类的声明有没有public并不重要
    protected String name;//protected当前类和子类可以访问
    protected String sex;
    protected int age;
    public Person(){    }//提供一个默认的构造方法,否则在子类会编译不通过
    public Person(String name,String sex,int age){//带参数的构造方法,目的是给类中的属性(成员变量)初始化
        //注意——类的构造方法不需要返回值,如果有返回值会报错!!!!
        this.name=name;
        this.sex=sex;
        this.age=age;
    }
    public void print(){//自定义方法print,目的是输出成员变量的值
        System.out.println("姓名:"+this.name);
        System.out.println("性别:"+this.sex);
        System.out.println("年龄:"+this.age);
    }
}
class Employee extends Person{//子类Employee使用extends继承父类Person
    protected String department;
    protected String positon;
     public Employee(String name,String sex,int age,String department,String positon){//带参构造方法,初始化子类和父类的成员变量
        this.name=name;
        this.sex=sex;
        this.age=age;
        this.department=department;
        this.positon=positon;
    }
    public void print1(){//新建方法print1,输出成员父类和子类的成员变量
        System.out.println("姓名:"+this.name);
        System.out.println("性别:"+this.sex);
        System.out.println("年龄:"+this.age);
        System.out.println("单位:"+this.department);
        System.out.println("位置:"+this.positon);
    }
  }
}

输出结果为:

                姓名:阿剑
                性别:男
                年龄:23
                -------------分割线-------------
                姓名:山河
                性别:男
                年龄:23
                单位:中国人民解放军陆军
                位置:新疆喀什

        上述代码中,子类Employee继承了父类 Person的name,sex,age属性,因此子类的方法print1可以直接调用其属性,同时子类在父类的基础上新增了属性department和position,实现了功能的拓展,如果在进行开发时,可以节约很多时间和内存资源。

4、多态——覆盖与重载

        er覆盖发生在父类和子类之间,当子类与父类的成员变量同名时,子类的成员变量会隐藏父类的成员变量,当子类与父类的成员方法拥有相同的名字、参数列表、返回值类型时,子类的方法会重写父类的方法,也叫方法的覆盖。

public class Main {
    public static void main(String[] args) {
        Employee p1=new Employee("张三",4000);
        p1.Print();
        System.out.println("p1.salary="+p1.salary);
        Manager p2=new Manager("李四",8720.5,"组织部");
        p2.Print();
        System.out.println("p2.salary="+p2.salary);
        Manager p3=new Manager("王五",9786.4,"市场部");
        p3.Print();
        System.out.println("p3.salary="+p3.salary);
    }
}
class Employee{//父类
    String name;
    int salary;//父类定义salary成员变量
    public Employee(){ }//无参构造方法
    public Employee(String name,int salary){//带两个参数的构造方法
        this.name=name;
        this.salary=salary;
    }
    public void Print(){//无参无返回值方法,有输出语句
        System.out.println("员工姓名:"+name+",员工工资:"+salary);
    }
}
class Manager extends Employee{//Manager继承父类Employee
    double salary;//子类也定义了成员变量salary,此时隐藏父类的salary成员变量
    String department;//子类新定义的成员变量
    public Manager(){  };//子类的无参构造方法
    public Manager(String name,double salary,String department){//带三个参数的构造方法
        this.name=name;
        this.salary=salary;
        this.department=department;
    }
    public void Print(){//这里重写了父类的Print方法
        System.out.println("经理姓名:"+name+"经理部门:"+department+"经理工资:"+salary);
    }
}

输出结果为:

                员工姓名:张三,员工工资:4000
                p1.salary=4000
                经理姓名:李四经理部门:组织部经理工资:8720.5
                p2.salary=8720.5
                经理姓名:王五经理部门:市场部经理工资:9786.4
                p3.salary=9786.4

        上述代码中,子类继承父类的同时,定义了一个与父类同名的成员变量salary,方法Print,此时在子类中父类的成员变量salary就被隐藏起来了,父类的方法Print被子类覆盖(重写),子类对象在调用时会忽略父类的salary成员变量和Print方法,这时发挥作用的是子类的salary变量和重写后的Print方法,这就是覆盖。覆盖发生有以下三点必要条件:

        1、发生在父类与子类之间

        2、必须具有相同的方法名,相同的返回值类型,相同的参数列表

        3、重写的方法不能比别重写的方法的访问权限更低

        另外,私有方法不能被覆盖,构造方法不能被覆盖,静态的方法不存在覆盖。

        方法的重载就是在一个类中定义多个同名的方法,但是每个方法的参数类型和参数个数都不相同,这样在调用时Java编译器就可以通过参数类型和参数个数来选择正确的方法。例:

public class Main {
    public static void main(String[] args) {
        A p=new A();
        p.Print();//调用了第一个Print方法
        p.Print(8000);//调用了第二个Print方法
        p.Print(3.0);//调用了第三个Print方法
    }
}
class A{
    public void Print(){//没有参数
        System.out.println("你好,世界!");
    }
    public void Print(int a){//拥有一个int类型的参数
        System.out.println("这是认识你的第"+a+"天!");
    }
    public void Print(double a){//拥有一个double类型的参数
        System.out.println("科技飞速发展,已经进入了Web"+a+"时代了!");
    }
}

输出结果为:

                你好,世界!
                这是认识你的第8000天!
                科技飞速发展,已经进入了Web3.0时代了!

        可以看到,类A定义了三个名为Print的方法,三个方法的参数列表不同,这里编译器就是根据参数个数的不同来选择正确的方法进行调用,从而得出正确的结果!由此可见,方法的重载要求同一类中方法名相同,而参数列表不同,即参数类型,参数个数,参数顺序至少有一项不同。另外,重载方法的返回值可以不同,方法的修饰符也可以不同。

结束,如有不当之处,恳请不吝赐教!

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐