java三大特性-封装

2,310 阅读15分钟

最近有个朋友说想要说最近在学java,他对面向对象的编程语言的时候还是有些不明白,为了帮他能更快地“转型”,我就写了这篇文章。因为从上层建筑层面而言。所有的面向对象编程语言的思路都是差不多的,而这三大特性,则是思路中的支柱点,接下来我就重点讲解了一下java三大特性。

面向对象的编程语言,拥有三大特性,分别是:“封装”,“继承”,“多态”。

封装

在面向对象编程中,封装封装(encapsulation)从字面上来理解就是包装的意思,是指利用抽象数据类型将数据和基于数据的操作封装在一起,使其构成一个不可分割的独立实体。其实就是将对象运行所需的方法和数据封装在程序公布其接口,数据被保护在抽象数据类型的内部,尽可能地隐藏内部的细节,只保留一些对外接口使之与外部发生联系。也就是说用户是无需知道对象内部的细节(当然也无从知道),但可以通过该对象对外的提供的接口来访问该对象,通俗点就是是其他附加到这些接口上的对象不需要关心对象实现的方法即可使用这个对象。这个概念就是“不要告诉我你是怎么做的,只要做就可以了“。

 所以封装把一个对象的属性私有化,同时提供一些可以被外界访问的属性的方法,如果不想被外界方法,我们大可不必提供方法给外界访问。但是如果一个类没有提供给外界访问的方法,那么这个类也没有什么意义了。

比如我们将一个对象看做是一个房子,里面的漂亮的壁纸,如沙发、电视、空调等都是该房子的私有属性,但是如果没有墙遮挡,那不就没有一点儿隐私了吗!就是因为有了遮挡的墙,我们既能够有自己的隐私 而且我们可以随意的更改里面的摆设而不会影响到其他的。但是如果没有门窗,一个包裹的严严实实的黑盒子,又有什么存在的意义呢?所以通过门窗别人也能够看到里面的风景。所以说门窗就是房子对象留给外界访问的接口。

一般在类里要将属性前添加private修饰符。然后定义getter和setter方法。然后在我们的 main 函数里的对象,不能再直接调用属性了,只能通过getter和setter方法进行调用。

封装的三大好处

1、良好的封装能够减少耦合。

2、类内部的结构可以自由修改。

3、可以对成员进行更精确的控制。

4、隐藏信息,实现细节。

修饰符

大家首先要先了解一下什么是修饰符,访问修饰符可以用来修饰属性和方法的访问范围。

在面向对象的过程中,我们通过权限控制对封装好的类加上权限,来限制外来者对类的操纵,借以达到保障类中数据和方法的安全的目的。可以这么说:一个类就是一个封装了相关属性及方法的逻辑实体。对于对象中的某些属性或者方法来说,它们可以是私有的,不能被外界访问。也可以是共有的,能够被外界任何人员访问。通过这种方式,对象对内部数据提供了不同级别的保护,以防止程序中无关的部分意外的改变或错误的使用了对象的私有部分,从而使得程序出现不要的错误。  

 java中4中修饰符分别为public、protectd、default、private。这就说明了面向对象的封装性,所有我们要尽量让权限降到最低,从而安全性提高。

如图,代表了不同的访问修饰符的访问范围,比如private修饰的属性或者方法,只能在本类中访问或者使用。什么修饰符都不加的话默认是default,默认在当前类中和同一包下都可以访问和使用。

访问权限       类        包       子类        其他包

public           ∨         ∨         ∨              ∨

protect         ∨         ∨         ∨              ×

default         ∨         ∨         ×              ×

private         ∨         ×         ×              × 


如果没有在属性前面添加任何修饰符,默认是default权限,我们通过创建对象就可以直接对属性值进行修改,没有体现封装的特性。这在程序设计中都是不安全的,所以我们需要利用封装,来改进我们的代码。


修饰符举例

首先我们先定义四个类Person,Parent,Teacher,Student,分别比较其他包,子类,包,本类的区别。每个类的位置图所示。


package com.java.test;
public  class Person {
    public String name = "张三";

    public void introduceMyself(){
        System.out.println(name);
    }
}

name是public的,若编译没有报错说明public变量拥有本类的访问权限。

package com.java.test;
public class Student {
    Person p =  new Person();
    public void test(){
        System.out.println(p.uname);
    }
}

Student和 Person在同一个包内,若编译没有报错,说明变量在相同拥有包内的访问权限。

package com.java.test1;
import com.java.test.Person;
public class Teacher extends Person {
    public int age;
    Person p = new Person();
    public void test1(){
        System.out.println(p.uname);
    }
}

Student和 Person不在同一个包内,但是Teacher继承了Person类,若编译没有报错,说明变量拥有子包内的访问权限

package com.java.test1;
import com.java.test.Person;
public class Parents {
    public String uname = "haha";
    Person p = new Person();
    public void test2(){
        System.out.println(p.uname);
    }
}

Parent和Person不在同一个包内,若编译没有报错,则说明变量拥有白外的访问权限

上面测试了之后,如果均能编译通过,就说明用public修饰的类在本类、同包、子类、其他包中互相访问都是可以的

同样开始测试protected权限问题,如果Person,Teacher,Student能编译通过,就说明用protected修饰的类在本类、同包、子类中互相访问都是可以的,而Parent编译不通过说明protected不可以在包外没有继承关系的类中互相访问。

同样开始测试default权限问题,如果Person,Student能编译通过,就说明用default修饰的类在本类、同包、子类中互相访问都是可以的,而Parent,Teacher编译不通过说明default修饰的类可以在包外不管有没有继承关系的类都不可以互相访问

同样开始测试private权限问题,如果Person能编译通过,就说明用private修饰的类在本类、同包、子类中互相访问都是可以的,而Parent,Teacher,Student编译不通过说明private修饰的类只能在本类中访问。

一般在类里要将属性前添加private修饰符。然后定义getter和setter方法。然后在我们的 main 函数里的对象,不能再直接调用属性了,只能通过getter和setter方法进行调用。

我先给大家讲一下包的作用

有时候会遇到程序的类名可能是重复的,我们就可以用包的概念来解决我们的问题。包的作用就是管理Java文件,解决同名文件冲突。这就和衣柜相类似。衣柜是不是有不同的隔断和抽屉,我们将衣服分门别类地放好,更有利与有利于我们管理。

定义一个包,我们使用package关键字,加上我们的包名。

package com.java.test;

//注意:必须放在源程序的第一行,包名可用”.”号隔开 ,包的命名规范是全小写字母拼写

Java系统中常用的包

    java.(功能).(类)
    java.lang.(类)  包含java语言基础的类
    java.util.(类)  包含语言中各种工具类
    java.io.(类) 包含输入、输出相关的类

 在不同包中使用另一个文件中的类,就需要用到import关键字。比如import com.java.test1.test.java,同时如果import com.java.test1*这是将包下的所有文件都导入进来。

this 关键字

一、this关键字主要有三个应用:

(1)this调用本类中的属性,也就是类中的成员变量;
(2)this调用本类中的其他方法;
(3)this调用本类中的其他构造方法,调用时要放在构造方法的首行。

Public Class Student { 
 public Student(String name) { //定义一个带形式参数的构造方法
 } public Student() { //定义一个方法,名字与类相同故为构造方法
  this(“Hello!”);
 } String name; //定义一个成员变量name
 private void SetName(String name) { //定义一个参数(局部变量)name
  this.name=name; //将局部变量的值传递给成员变量
 }
}

如上面这段代码中,有一个成员变量name,同时在方法中有一个形式参数,名字也是name,然后在方法中将形式参数name的值传递给成员变量name。

this这个关键字其代表的就是对象中的成员变量或者方法。也就是说,如果在某个变量前面加上一个this关键字,其指的就是这个对象的成员变量或者方法,而不是指成员方法的形式参数或者局部变量。为此在上面这个代码中,this.name代表的就是对象中的成员变量,又叫做对象的属性,而后面的name则是方法的形式参数,代码this.name=name就是将形式参数的值传递给成员变量。

如果一个类中有多个构造方法,因为其名字都相同,跟类名一致,那么这个this到底是调用哪个构造方法呢?其实,这跟采用其他方法引用构造方法一样,都是通过形式参数来调用构造方法的。如上例中,this关键字后面加上了一个参数,那么就表示其引用的是带参数的构造方法。如果现在有三个构造方法,分别为不带参数、带一个参数、带两个参数。那么Java编译器会根据所传递的参数数量的不同,来判断该调用哪个构造方法。从上面示例中可以看出,this关键字不仅可以用来引用成员变量,而且还可以用来引用构造方法。

内部类

内部类( Inner Class )我们从外面看是非常容易理解的,内部类就是将一个类的定义放在另一个类的定义内部。当然与之对应,包含内部类的类被称为外部类。

很多初学者一定会问那为什么要将一个类定义在另一个类里面呢?

我们程序设计中有时候会存在一些使用接口很难解决的问题,这时我们就可以利用内部类提供的、可以继承多个具体的或者抽象的类的能力来解决这些程序设计问题。可以这样说,接口只是解决了部分问题,而内部类使得多重继承的解决方案变得更加完整。

在《Think in java》中有这样一句话:使用内部类最吸引人的原因是:每个内部类都能独立地继承一个(接口的)实现,所以无论外围类是否已经继承了某个(接口的)实现,对于内部类都没有影响。

public interface Father {

}

public interface Mother {

}

public class Son implements Father, Mother {

}

public class Daughter implements Father{

    class Mother_ implements Mother{
        
    }
}

内部类特性

1、内部类可以用多个实例,每个实例都有自己状态信息,并且与其他外围对象信息相互独立。

2、在单个外围类中,可以让多个内部类以不同的方式实现同一个接口,或者继承同一个类。

3、创建内部类对象的时刻并不依赖于外围类对象的创建。

4、内部类并没有令人迷惑的“is-a”关系,他就是一个独立的实体。

5、内部类提供了更好的封装,除了该外围类,其他类都不能访问。

package com.java.test;

public class OuterClass {
    private String name ;
    private int age;

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
    public void display(){
        System.out.println("调用的是OuterClass的display");
    }
    public class InnerClass{
        public InnerClass(){
            name = "chenssy";
            age = 23;
        }

        public OuterClass getOuterClass(){
            return OuterClass.this;
        }
        public void display(){
            System.out.println("name:" + getName() +"   ;age:" + getAge());
        }
    }

    public static void main(String[] args) {
        OuterClass outerClass = new OuterClass();
        OuterClass.InnerClass innerClass = outerClass.new InnerClass();
        innerClass.display();
        innerClass.getOuterClass().display();
    }
}

name:chenssy   ;age:23
调用的是OuterClass的display

我们需要明确一点,内部类是个编译时的概念,一旦编译成功后,它就与外围类属于两个完全不同的类(当然他们之间还是有联系的)。

我们还看到了如何来引用内部类:引用内部类我们需要指明这个对象的类型:OuterClasName.InnerClassName。同时如果我们需要创建某个内部类对象,必须要利用外部类的对象通过.new来创建内部类: OuterClass.InnerClass innerClass = outerClass.new InnerClass();。

同时如果我们需要生成对外部类对象的引用,可以使用OuterClassName.this,这样就能够产生一个正确引用外部类的引用了。

在Java中内部类主要分为成员内部类、局部内部类、匿名内部类、静态内部类。

成员内部类

成员内部类也是最普通的内部类,它是外围类的一个成员,所以他是可以无限制的访问外围类的所有 成员属性和方法,尽管是private的,但是外围类要访问内部类的成员属性和方法则需要通过内部类实例来访问。

在成员内部类中要注意两点,

  • 成员内部类中不能存在任何static的变量和方法;
  • 成员内部类是依附于外围类的,所以只有先创建了外围类才能够创建内部类。

public class OuterClass {
    private String str;
    
    public void outerDisplay(){
        System.out.println("outerClass...");
    }
    
    public class InnerClass{
        public void innerDisplay(){
            //使用外围内的属性
            str = "chenssy...";
            System.out.println(str);
            //使用外围内的方法
            outerDisplay();
        }
    }
    
    /*推荐使用getxxx()来获取成员内部类,尤其是该内部类的构造函数无参数时 */
    public InnerClass getInnerClass(){
        return new InnerClass();
    }
    
    public static void main(String[] args) {
        OuterClass outer = new OuterClass();
        OuterClass.InnerClass inner = outer.getInnerClass();
        inner.innerDisplay();
    }
}

个人推荐使用getxxx()来获取成员内部类,尤其是该内部类的构造函数无参数时 。 

句局部内部类

局部内部类,是嵌套在方法和作用域内的,对于这个类的使用主要是应用与解决比较复杂的问题,想创建一个类辅助我们的解决方案,到那时不希望这个类是公共的,所以我们就可以创建局部内部类。

局部内部类和成员内部类一样被编译,他只能在该方法和属性中使用,不在该方法和属性就会失效。

  定义在方法里:

public class Parcel5 {
    public Destionation destionation(String str){
        class PDestionation implements Destionation{
            private String label;
            private PDestionation(String whereTo){
                label = whereTo;
            }
            public String readLabel(){
                return label;
            }
        }
        return new PDestionation(str);
    }
    
    public static void main(String[] args) {
        Parcel5 parcel5 = new Parcel5();
        Destionation d = parcel5.destionation("chenssy");
    }
}

定义在作用域内:

public class Parcel6 {
    private void internalTracking(boolean b){
        if(b){
            class TrackingSlip{
                private String id;
                TrackingSlip(String s) {
                    id = s;
                }
                String getSlip(){
                    return id;
                }
            }
            TrackingSlip ts = new TrackingSlip("chenssy");
            String string = ts.getSlip();
        }
    }
    
    public void track(){
        internalTracking(true);
    }
    
    public static void main(String[] args) {
        Parcel6 parcel6 = new Parcel6();
        parcel6.track();
    }
}

匿名内部类

public class OuterClass {
    public InnerClass getInnerClass(final int num,String str2){
        return new InnerClass(){
            int number = num + 3;
            public int getNumber(){
                return number;
            }
        };        /* 注意:分号不能省 */
    }
    
    public static void main(String[] args) {
        OuterClass out = new OuterClass();
        InnerClass inner = out.getInnerClass(2, "chenssy");
        System.out.println(inner.getNumber());
    }
}

interface InnerClass {
    int getNumber();
}


1、 匿名内部类是没有访问修饰符的。

2、 new 匿名内部类,这个类首先是要存在的。如果我们将那个InnerClass接口注释掉,就会出现编译出错。

3、 注意getInnerClass()方法的形参,第一个形参是用final修饰的,而第二个却没有。同时我们也发现第二个形参在匿名内部类中没有使用过,所以当所在方法的形参需要被匿名内部类使用,那么这个形参就必须为final。

4、 匿名内部类是没有构造方法的。因为它连名字都没有何来构造方法。

静态内部类

static可以修饰成员变量,方法,代码块,其他还可以修饰内部类,使用static修饰的内部类我们称之为静态内部类,不过我们更喜欢称之为嵌套内部类。静态内部类与非静态内部类之间最大的区别就是非静态内部类在编译完成之后会隐含的保存着一个引用,该以及用是指向他的外围内。但是静态内部类却没有,这就意味着静态内部类创建不需要依赖外围类,并且他不能使用任何外围类的非static成员变量和方法。

public class OuterClass {
    private String sex;
    public static String name = "chenssy";
    
    /**
     *静态内部类
     */
    static class InnerClass1{
        /* 在静态内部类中可以存在静态成员 */
        public static String _name1 = "chenssy_static";
        
        public void display(){
            /* 
             * 静态内部类只能访问外围类的静态成员变量和方法
             * 不能访问外围类的非静态成员变量和方法
             */
            System.out.println("OutClass name :" + name);
        }
    }
    
    /**
     * 非静态内部类
     */
    class InnerClass2{
        /* 非静态内部类中不能存在静态成员 */
        public String _name2 = "chenssy_inner";
        /* 非静态内部类中可以调用外围类的任何成员,不管是静态的还是非静态的 */
        public void display(){
            System.out.println("OuterClass name:" + name);
        }
    }
    
    /**
     * @desc 外围类方法
     * @author chenssy
     * @data 2013-10-25
     * @return void
     */
    public void display(){
        /* 外围类访问静态内部类:内部类. */
        System.out.println(InnerClass1._name1);
        /* 静态内部类 可以直接创建实例不需要依赖于外围类 */
        new InnerClass1().display();
        
        /* 非静态内部的创建需要依赖于外围类 */
        OuterClass.InnerClass2 inner2 = new OuterClass().new InnerClass2();
        /* 方位非静态内部类的成员需要使用非静态内部类的实例 */
        System.out.println(inner2._name2);
        inner2.display();
    }
    
    public static void main(String[] args) {
        OuterClass outer = new OuterClass();
        outer.display();
    }
}