【阿里云】Java面向对象开发课程笔记(一)——类与对象

1. 面向对象简介

  面向对象是一种程序设计方法,但不表示世界上所有的开发者都认同面向对象。因为有很多开发者认为面向对象过于复杂,所以更多人愿意使用函数式编程。
  面向对象的前身属于面向过程,如果想要笼统的解释这两种开发模式的区别,最好的例子:面向过程是解决问题,而面向对象是模块化设计。对于现在的程序开发,更多情况下就像汽车组装一样,不同的工厂生产不同的配件。将所有的零件组装在一起,就可以想成一辆汽车,并且当某一个零件坏了的时候,还可以进行配装。面向过程缺少了可重用设计。
面向对象的基本特征:

  • 封装性:内部的操作对外部而言不可见;
  • 继承性:在上一辈基础上继续发展;
  • 多态性:这是最重要的一个环节,利用多态性才可以得到良好的设计。子类对象可以直接赋值给父类对象,但运行是时依然表现出子类的行为特征。

  在面向对象开发的时候有三个阶段:OOA(面向对象分析)、OOD(面向对象设计)、OOP(面向对象编程)。
  所有的程序的开发原则都离不开实际生活,从程序的开发角度讲,面向对象可以进行生活的抽象。面向对象的概念需要通过大量的代码进行理解和验证,在Java中不光要考虑面向对象,还要考虑内存分配问题。

2. 类与对象

  类与对象是整个面向对象设计的核心所在,也是所有概念的基础。类本身属于引用数据类型,所以类的使用上会牵扯到内存的分配。

2.1 类与对象基本定义

  所谓的类描述的就是共性特征。任何时候只是依靠共性的特征是无法进行精准描述的,那么就需要一些个性化特点。就可以描述一个个独立的事物。所以这样就可以推断出:类是一个共性的概念,而对象指的是一个具体的可以使用的事物。
  很明显,在实际的开发过程中一定需要首先产生类,而后才可以产生对象。那么对象的所有操作行为都一定在类中进行的完整定义。类中没有定义的功能,对象一定无法使用。
  类中的组成:方法(操作的行为)、属性(变量,描述每一个对象的具体特点)。

2.2 类与对象定义及使用

  如果在程序之中要进行类的定义可以使用class关键字完成,定义语法如下:

类的定义语法
class 类名称 { // 所有的程序都要求以“ {} ”作为分界符
      属性 1;
      属性 2;
      方法();
    }

此时的方法不再由主类进行调用,而是要通过对象进行调用。
范例:定义类

代码

class Person {  // 定义一个类,类名称每个单词首字母要求大写
          String name; // 表示人的姓名
          int age;  // 表示人的年龄
          public void info () {
                  System.out.println ("name = " + name + ",age = " + age);
          }
}

  在这个类中,只定义了两个属性和一个方法,需要注意的是,类中可以定义的属性数量和方法数量是没有限制的,而且在编写的时候,每一个方法中的代码尽量不要特别长。
  类定义完成之后是不能够直接去使用的,如果要想使用类那么必须产生对象,而对象的定义分为以下两种语法形式:

  • 声明并实例化对象:类名称 对象名称 = new 类名称();
  • 分步进行对象实例化:
    • 声明对象:类名称 对象名称 = null;
    • 实例化对象:对象名称 = new 类名称();

  引用数据类型的最大特征在于内存的分配操作,而只要出现有关键字new,那么就只有一个解释:开辟新内存(内存不可能无限开辟,所以所谓的性能调优调整的就是内存问题)。
  所有的对象只有在实例化之后才可以真正使用。而对象的使用都是围绕着类进行的,那么此时就有两种形式:

  • 调用类中属性:对象.属性 = 内容;
  • 调用类中的方法:对象.方法();

范例:声明并实例化对象

代码

class Person {  // 定义一个类,类名称每个单词首字母要求大写
          String name; // 表示人的姓名
          int age;  // 表示人的年龄
          public void info () {
                  System.out.println ("name = " + name + ",age = " + age);
          }
}

public class Test {
     public static void main (String args[]) {
             // 类名称 对象名称 = new 类名称();
             Person per = new Person(); // 实例化了一个per对象
             per.name = "张三"; // 设置对象中属性
             per.age = 18; // 设置对象中属性
             per.info(); // 调动类中的方法
  }
} // 输出结果:name = 张三 、 age = 18

  以上就实现了一个最基本的类的定义以及对象的使用操作形式。

2.3 对象内存分析

  如果要想进行对象的产生分析,那么首先就必须清楚引用类型。引用类型指的是内存空间的操作。对于现在的内存,主要使用两块内存空间:

  • 堆内存空间:保存真正的数据,堆内存保存的是对象的属性信息。
  • 栈内存空间:保存堆内存的地址,堆内存操作权,可以简单理解为保存的是对象名称,一个栈内存只能保存一个堆内存地址。

2.2中代码内存参考图如下:
这里写图片描述

  对于对象的产生一共会有两种格式,现在使用的是声明并实例化对象方式,也可以使用分步方式完成。

代码

public class Test {
     public static void main (String args[]) {             
             Person per = null; // 声明一个新的对象
             per = new Person(); // 实例化对象per
             per.name = "张三"; // 设置对象中属性
             per.age = 18; // 设置对象中属性
             per.info(); // 调动类中的方法
     }
}        

内存分析如下图:
这里写图片描述

千万记住:对象(所有的引用数据类型)必须在其开辟空间之后才可以使用。如果使用了为开辟内存空间的引用数据类型,则将出现NullPointerException。

代码

class Person {  // 定义一个类,类名称每个单词首字母要求大写
          String name; // 表示人的姓名
          int age;  // 表示人的年龄
          public void info () {
                  System.out.println ("name = " + name + ",age = " + age);
          }
}

public class Test {
     public static void main (String args[]) {             
             Person per = null; // 声明一个新的对象
             per.name = "张三"; // 设置对象中属性
     }
}     

  这个时候只声明了对象,而没有为其开辟对内存空间,此时编译不会出错,但是执行后会报出如下错误提示:

错误提示
Exception in thread “main” java.lang.NullPointerExceptionat Test.main(Test.java:13)

  NullPointerException,只有引用数据类型(数组、类、接口),才能产生此类异常,以后出这类错误,就在错误位置观察该对象是否实例化。

2.4 引用传递初次分析

  对于初学者来说最难的部分就是引用传递分析。以后的开发都是引用传递。
  引用的本质就在于别名,而这个这个别名只不过是放在了栈内存之中,即:一块堆内存可以被多个栈内存所指向。
范例:观察一个引用传递

代码

class Person {  // 定义一个类,类名称每个单词首字母要求大写
          String name; // 表示人的姓名
          int age;  // 表示人的年龄
          public void info () {
                  System.out.println ("name = " + name + ",age = " + age);
          }
}

public class Test {
     public static void main (String args[]) {
             // 类名称 对象名称 = new 类名称();
             Person per1 = new Person(); // 实例化了一个per1对象
             per1.name = "小于子"; // 设置对象中属性
             per1.age = 30; // 设置对象中属性
             // 此步骤就是引用传递的操作
             Person per2 = per1;  // 采用同样的类型接收
             per2.name = "狗剩"; // 设置一个名字
             per1.info(); // 调动类中的方法
  }
}  // name = 狗剩,age = 30

内存分析如下图:
这里写图片描述

范例:观察一个引用传递

代码

class Person {  // 定义一个类,类名称每个单词首字母要求大写
          String name; // 表示人的姓名
          int age;  // 表示人的年龄
          public void info () {
                  System.out.println ("name = " + name + ",age = " + age);
          }
}

public class Test {
     public static void main (String args[]) {
             // 类名称 对象名称 = new 类名称();
             Person per1 = new Person(); // 实例化了一个per1对象
             per1.name = "小于子"; // 设置对象中属性
             per1.age = 30; // 设置对象中属性
             Person per2 = new Person();  
             per2.name = "张三"; 
             per2.age = 20;
             // 此步骤就是引用传递的操作
             per2 = per1;  // 采用同样的类型接收
             per2.name = "狗剩"; // 设置一个名字
             per1.info(); // 调动类中的方法
  }
}  // name = 狗剩,age = 30

  在程序开发过程中,所谓的垃圾空间指的是没有任何栈内存指向的堆内存空间,所有的垃圾空间将不定期被Java中的垃圾收集器(GC,Garbage Collector)回收以实现内存空间的释放,不过从实际开发来讲,虽然Java会提供GC,但是GC会造成程序性能的下降,所以开发过程里一定要控制好对象的产生数量,即:无用的对象尽可能少产生。

3. private实现封装处理

  封装是整个Java最复杂的概念,本次讲解的也只是最基本的概念。
  如果没有封装会怎么样?
范例:观察如下的一段程序

代码

public class Test {
     public static void main (String args[]) {
             Person per = new Person();
             per.age = -200; // 年龄设置为-200
  }
} 

  这时程序不会出现任何语法错误,int的取值允许取-200,但是没有一个人的年龄会是-200,这就属于业务逻辑出错。
  此时若想回避此类问题,那么首先要解决的就是如何让对象不能够直接操作年龄的属性,或者指的是让类的外部如何不能够操作类中的敏感内容。所以此时解决问题的核心观念:如何让内部的操作对外部不可见,此时就可利用private关键字来实现。
范例:利用private类实现封装

代码

class Person { 
          private String name; 
          private int age;  
          public void info () {
                 System.out.println ("name = " + name + ",age = " + age);
          }
}

public class Test {
     public static void main (String args[]) {
             Person per = new Person(); 
             per.name = "张三"; 
             per.age = -200;
             per.info();
  }
} 

  类中的属性和方法都可用private定义,但是很少在方法上使用private。一旦属性的声明上使用了private定义之后,那么如果其它类直接进行该属性访问的时候,就将出现如下错误提示:

错误提示
Test.java:13: 错误: name可以在Person中访问private per.name = “张三”;
Test.java:14: 错误: age可以在Person中访问private per.age = -200;

  此时使用了private声明之后属性安全了,外部无法直接操作了,但是新的问题又来了。现在如果想要进行private私有属性的访问,按照Java的设计原则,就可以使用setter、getter方法。

  • setter方法:主要用于进行属性内容设置,
    • private String name: public void setName(String n);
  • getter方法:主要用于属性内容的取得
    • private String name: public void getName();

范例:扩展Person类中的内容

代码

class Person { 
          private String name; 
          private int age;  
          public void setName(String n){
                    name = n;    
          }
          public void setAge(int a){
                    if (age >= 0 && age <= 200) { //检测年龄
                                age = a;    
                    }else{
                               age = 0;
                    }    
          }

          public String getName(){
                    return name;   
          } 
          public int getAge(){
                    return age;   
          } 


          public void info () {
                 System.out.println ("name = " + name + ",age = " + age);
          }
}

public class Test {
     public static void main (String args[]) {
             Person per = new Person(); 
             per.setName("张三"); 
             per.setAge(200);
             per.info();
  }
} // 输出结果:name = 张三,age = 0

  如果非要进行检测,可以在setter里完成,但是意义不大。
类的设计原则:在编写类的时候类的属性必须使用private封装。而使用private封装的属性,如果需要被外部所使用,那么就按照格式定义相应的setter、getter方法。
  private实现封装的最大特征在于:只允许本类访问,不允许外部访问。且private只是封装的第一步。

4. 构造方法与匿名对象

  实例化对象产生格式:

  • ①类名称 ②对象名称 = ③new ④类名称()

对于以上格式观察组成部分:

  • ①类名称:任何对象都应有其对应的类,因为没有类就不知道对象具备的功能。
  • ②对象名称:是一个唯一的标记,表示的是以后操作的属性的标记;
  • ③new:表示开辟新的堆内存空间
  • ④类名称():构造方法

  构造方法指的是在使用关键字new实例化新对象的时候类进行调用的操作方法,定义构造方法的原则如下:方法名称必须与类名称相同,并且构造方法没有返回值类型声明,同时,每一个类中一定会至少存在有一个构造方法,如果类中没有明确的定义任何一个构造方法,那么将自动生成一个无参的,什么都不做的构造方法。

范例:定义一个无参的、什么都不做的构造方法

代码

class Person { 
          private String name; 
          private int age;  
          // 构造方法名称与类名称相同,并且没有返回值类型声明
         public Person() {       // 如果你现在的类中不写词定义也会在变异后自动生成
                         System.out.println("************************"); 
         }
          public void setName(String n){
                    name = n;    
          }
         public void setAge(int a){
                    if (age >= 0 && age <= 200) { //检测年龄
                                age = a;    
                    }else{
                               age = 0;
                    }    
          }
          public String getName(){
                    return name;   
          } 
          public int getAge(){
                    return age;   
          } 

          public void info () {
                 System.out.println ("name = " + name + ",age = " + age);
          }
}

public class Test {
     public static void main (String args[]) {
             Person per = new Person(); 
             per.setName("张三"); 
             per.setAge(200);
             per.info();
  }
} // 输出结果:
// ************************
// name = 张三,age = 0

  构造方法在new时调用,且一个对象只能new一次,也就是说只能调用一次。
疑问:既然构造方法中没有返回数据,为什么不使用void定义?

  • 现在类中的组成:属性、普通方法、构造方法
    • 属性是在对象开辟堆内存的时候就开辟的空间;
    • 构造方法是在使用关键字new同时调用的;
      • public Person();
    • 普通方法是在对象已经实例化完成了(空间开辟了、构造方法执行了)再调用的,可以调用多次;
      • 普通方法:public void Person();

  对于类中可以自动生成的无参构造方法前提:类中没有定义任何构造方法,如果类中已经定义了构造方法,那么此类默认的构造方法将不会自动生成。

范例:类中定义一个有参构造方法

代码

class Person { 
          private String name; 
          private int age;  
          // 定义一个有参构造,则默认的无参构造将不会在编译时自动生成
         public Person(String n,int a) {    
                   name = n; // 也可以写setName(n);
                   setAge(a);
         }
          public void setName(String n){
                    name = n;    
          }
         public void setAge(int a){
                    if (age >= 0 && age <= 200) { // 检测年龄
                                age = a;    
                    }else{
                               age = 0;
                    }    
          }
          public String getName(){
                    return name;   
          } 
          public int getAge(){
                    return age;   
          } 

          public void info () {
                 System.out.println ("name = " + name + ",age = " + age);
          }
}

public class Test {
     public static void main (String args[]) {
            // Person per = new Person(); 此代码出错
             Person per = new Person("张三"20);
             per.info();
  }
} // 输出结果:name = 张三,age = 20

构造方法作用:

  • 构造方法的调用和对象的内存分配几乎是同步完成的,l利用构造方法可以对类中的属性进行初始化处理。
  • 通过构造方法设置内容可以避免重复的setter调用。
    • setter方法住了具备有设置内容之外,也可以承担修改数据的操作。eg:Person per = new Person(“张三”,20); per.setAge(18);

  构造方法也可以重载,构造方法名与类名相同,无返回值类型,因此只能实现参数的类型或个数不同的重载。
范例:构造方法重载

代码

class Person { 
          private String name; 
          private int age;  

          public Person() {}
          public Person(String n){
                   name = n;
          }

          public Person(String n,int a) {    
                   name = n; // 也可以写setName(n);
                   setAge(a);
         }
          public void setName(String n){
                    name = n;    
          }
         public void setAge(int a){
                    if (age >= 0 && age <= 200) { // 检测年龄
                                age = a;    
                    }else{
                               age = 0;
                    }    
          }
          public String getName(){
                    return name;   
          } 
          public int getAge(){
                    return age;   
          } 

          public void info () {
                 System.out.println ("name = " + name + ",age = " + age);
          }
}

public class Test {
     public static void main (String args[]) {
             Person per = new Person("张三");
             per.setAge(18);
             per.info();
  }
} // 输出结果:name = 张三,age = 18

  重载时注意定义结构:建议若干个构造方法按照参数个数的升序或降序排列。
  请按照如下顺序进行类定义:①属性;②构造方法;③普通方法。

范例:匿名对象

代码

public class Test {
     public static void main (String args[]) {
              new Person("张三",20).info();
  }
} // 输出结果:name = 张三,age = 20

  匿名对象不会被任何栈内存指向,使用一次后就会成为垃圾。
总结:
1.构造方法每个类中至少存在一个;
2.构造方法名称与类名称相同,无返回值类型;
3.构造方法重载只考虑参数的类型或个数不同即可。

5. 综合案例:简单Java类

简单Java开发要求:

  • 类名称应该有意义,可以准确地描述出某一类事物,例如:Emp、Member、Cat;
  • 类中所有属性必须使用private封装,所有的属性必须按要求提供有setter和getter方法;
  • 类中可以定义若干个构造方法,但是必须保留一个无参构造方法;
  • 类中的所有方法都不允许出现Syetem.out语句,所有的输出必须交给调用处完成;
  • 类中应该有一个返回类完整信息的方法,这个方法名称暂时未getInfo();

范例:编写程序类

代码

class Emp {  // 类名称可以明确描述出某一类事物
          private int empno;
          private String ename;
          private String job;
          private double sal;
          private double comm;

           public void setEmpno(int eno) {
                     empno = eno;
            }
           public void setEname(String ena) {
                     ename = ena;
            }
            public void setJob(String j) {
                       job = j;  
            }
             public void setSal(double s) {
                        sal = s;
            }
            public void setComm(double c) {
                        comm = c;
            }

             public int getEmpno() {
                          return empno;
            } 
             public String getEname() {
                          return ename;
            }
            public String getJob() {
                         return job;       
            }
             public double getSal() {
                         return sal;
            }
             public double getComm() {
                           return comm;
            }

            public String getInfo() {
                return "empno = " + empno +  "\n" + "ename = " + ename +  "\n" + "job = " + job +  "\n" + "sal = " + sal +  "\n" + "comm = " + comm;
             }

             public Emp(){};
             public Emp(int eno,String ena,String j)
{
                     setEmpno(eno);
                     setEname(ena);
                     setJob(j);
             }
 }

public class Test {
     public static void main (String args[]) {
                System.out.println(new Emp(1234,"SJZ","CLERK").getInfo());
   }
} /* 输出结果:
empno = 1234
ename = SJZ
job = CLERK
sal = 0.0
comm = 0.0     */
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值