Java面向对象开发学习笔记(二)

课时4 类与对象(对象内存分析)

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

  • 堆内存空间:保存真正的数据,堆内存保存的是对象的属性信息;
  • 栈内存空间:保存的堆内存的地址,即堆内存的操作权,如果要想简化理解,可以理解为保存的是对象名称。
class Person{
    //定义一个类,类名称每个单词首字母要求大写
    String name; //表示人的姓名
    int age; //表示人的年龄
    public void info(){
        System.out.println("name = " + name + "、age = " + age) ;
    }
}
public class TestDemo{
    public static void main(String args[]){
        //类名称 对象名称 = new 类名称 ;
        Person per = new person() ; //此时就表示实例化了一个per对象
        per.name = "张三" ; //设置对象中的属性
        per.age = 18 ; //设置对象中的属性
        per.info() ; //调用类中的方法
    }
}

所以按照之前的程序,就可以给出如下的内存参考图:

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

class Person{
    //定义一个类,类名称每个单词首字母要求大写
    String name; //表示人的姓名
    int age; //表示人的年龄
    public void info(){
        System.out.println("name = " + name + "、age = " + age) ;
    }
}
public class TestDemo{
    public static void main(String args[]){
        Person per = null ; //声明一个新的对象
        per = new Person(); //实例化对象
        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 TestDemo{
    public static void main(String args[]){
        Person per = null ; //声明一个新的对象
        per.name = "张三" ; //设置对象中的属性
        per.age = 18 ; //设置对象中的属性
        per.info() ; //调用类中的方法
    }
}

这个时候只声明了对象,而并没有为其开辟堆内存空间,而本程序在编译的时候不会出现任何的语法错误,但是在执行的时候会出现如下的错误提示:

Exception in thread "main" java. lang.NullPointerException

at TestDemo.main(TestDemo.java:12)

NullPointerException是开发过程中一直陪伴到最后的一个异常。只有引用数据类型才会产生此类异常,一旦出现就根据错误的位置观察该对象是否以实例化。

课时5 类与对象(引用传递初次分析)

所有初学者最难的部分就是引用传递的分析。以后的开发之中都是引用传递。

引用的本质就在于别名,而这个别名只是放在了栈内存之中,即:一块堆内存可以被多个栈内存所指向。

范例:观察引用传递

class Person{
    //定义一个类,类名称每个单词首字母要求大写
    String name; //表示人的姓名
    int age; //表示人的年龄
    public void info(){
        System.out.println("name = " + name + "、age = " + age) ;
    }
}
public class TestDemo{
    public static void main(String args[]){
        Person per1 = new Person ; //声明一个新的对象
        per1.name = "小于子" ; //设置对象中的属性
        per1.age = 30 ; //设置对象中的属性
        //此步骤就是引用传递的操作
        Person per2 = per1 ;  //采用同样的类型接收
        per2.name = "狗剩" ; //设置一个名字
        per1.info() ; //调用类中的方法
    }
}

此时通过内存关系图来进行程序运行的观察:

另一种情况:

class Person{
    //定义一个类,类名称每个单词首字母要求大写
    String name; //表示人的姓名
    int age; //表示人的年龄
    public void info(){
        System.out.println("name = " + name + "、age = " + age) ;
    }
}
public class TestDemo{
    public static void main(String args[]){
        Person per1 = new Person ; //声明一个新的对象
        Person per2 = new Person ;
        per1.name = "小于子" ; //设置对象中的属性
        per1.age = 30 ; //设置对象中的属性
        per2.name = "张三" ; //设置对象中的属性
        per2.age = 20 ; //设置对象中的属性
        //此步骤就是引用传递的操作
        per2 = per1 ;  //采用同样的类型接收
        per2.name = "狗剩" ; //设置一个名字
        per1.info() ; //调用类中的方法
    }
}

观察此时的内存分析图:

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

课时6 private实现封装处理

面向对象有三大特征:封装性、继承性、多态性。封装是整个Java中最复杂的概念,而我们本次讲解的也只是封装的一个基本概念。

想要弄清封装,首先必须清楚没有封装会怎么样?

范例:观察如下的一段程序:

class Person{
    //定义一个类,类名称每个单词首字母要求大写
    String name; //表示人的姓名
    int age; //表示人的年龄
    public void info(){
        System.out.println("name = " + name + "、age = " + age) ;
    }
}
public class TestDemo{
    public static void main(String args[]){
        //类名称 对象名称 = new 类名称 ;
        Person per = new person() ; //此时就表示实例化了一个per对象
        per.name = "张三" ; //设置对象中的属性
        per.age = -200 ; //设置对象中的属性
        per.info() ; //调用类中的方法
    }
}

这个时候实际上不会出现任何语法错误,因为从int型的数据保存范围来讲,是允许保存负数的。但是不会有一个人的年龄是-200岁,那么也就证明这个时候属于业务逻辑出错。

此时如果想要回避此类问题,那么首先要解决的就是如何让对象不能直接操作年龄的属性,或者指的是让类的外部如何不能够操作类中的敏感内容。此时解决问题的核心观念就在于如何让内部的操作对外部不可见,此时就可以利用private关键字来实现。

范例:利用private来实现封装

class Person{
    //定义一个类,类名称每个单词首字母要求大写
    private String name; //表示人的姓名
    private int age; //表示人的年龄
    public void info(){
        System.out.println("name = " + name + "、age = " + age) ;
    }
}
public class TestDemo{
    public static void main(String args[]){
        //类名称 对象名称 = new 类名称 ;
        Person per = new person() ; //此时就表示实例化了一个per对象
        per.name = "张三" ; //设置对象中的属性
        per.age = -200 ; //设置对象中的属性
        per.info() ; //调用类中的方法
    }
}

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

错误:name可以在Person中访问private

per.name = "张三" ;

错误: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(String n);

范例:扩展Person类中的内容。

class Person{
    //定义一个类,类名称每个单词首字母要求大写
    private String name; //表示人的姓名
    private int age; //表示人的年龄
    public void setName(String n){
        name = n ;
    }
    public void setAge(int a){
        if (a >= 0 && a <= 250){
        age = a ;
        }
        age = 0 ;
    }
    public String getName(){
        return name ;
    }
    public int getAge(){
        return age ;
    }
    public void info(){
        System.out.println("name = " + name + "、age = " + age) ;
    }
}
public class TestDemo{
    public static void main(String args[]){
        //类名称 对象名称 = new 类名称 ;
        Person per = new person() ; //此时就表示实例化了一个per对象
        per.setName("张三") ; //设置对象中的属性
        per.setAge(-200); //设置对象中的属性
        per.info() ; //调用类中的方法
    }
}

如果要进行检测,那么可以在setter里完成。

类的设计原则

以后在编写类的时候,类中的所有属性必须使用private封装。而使用private封装的属性,如果需要被外部使用,那么就按照格式定义相应的setter、getter方法。

private实现封装的最大特征在于,只允许本类访问而不允许外部访问。

private只是封装的第一步。

posted @ 2018-03-21 13:50  笆篱公社  阅读(169)  评论(0编辑  收藏  举报