码迷,mamicode.com
首页 > 其他好文 > 详细

面向对象

时间:2016-07-11 07:54:25      阅读:197      评论:0      收藏:0      [点我收藏+]

标签:

类和对象

面向对象两个重要概念:类(class)和对象(object,也称为实例:instance)。

类是某一批对象的抽象,可以把类理解成某种概念;对象才是一个具体存在的实体。类和对象是面向对象的核心。

定义一个类,类名是一个合法的标识符就可以。为了可读性,建议用一个或多个有意义的单词连缀,每个单词首字母大写,其它小写,单词与单词之间不能有任何分隔符。

定义类可以包含三种最常见成员:构造器,成员变量和方法。

static修饰的成员不能访问没有static修饰的成员。

Java语言通过new关键字调用构造器。

如果程序员没有为一个类编写构造器,系统会为该类提供一个默认的构造器。程序员为类提供了,系统就不会添加。

定义成员变量

[修饰符] 类型 成员变量名 [=默认值]

 成员变量第一个单词首字母小写,后面每个单词首字母大写。

定义方法

[修饰符] 方法返回值类型 方法名(形参列表){
  //由零条到多条可执行性语句组成的方法体  
}

 

如果声明了方法返回值类型,则方法体内必须有一个有效的return语句。没有返回值,必须使用void来声明。

方法的命名规则和成员变量的命名规则基本相同。

static是一个特殊的关键字,它可用于修饰方法、成员变量等成员。static修饰的成员表明它属于这个类本身,而不属于该类的单个实例。通常把static修饰的成员变量和方法称为类变量和类方法。不用static修饰的成员变量和方法也被称为实例变量和实例方法。

static主要的作用是用于区分成员变量、方法、内部类、初始化块这四种成员是属于类本身还是属于实例。

构造器是一个特殊的方法,定义构造器

[修饰符] 构造器名 (形参列表){
  //由零条或多条可执行性语句组成的构造器执行体  
}

构造器必须和类名相同。注:构造器既不能定义返回值类型,也不能使用void声明构造器没有返回值。如果为构造器定义返回值类型,或者用void定义,编译不会出错,Java会把这个构造器当成方法来处理——它不再是构造器。

this可以在return语句中返回调用该方法的对象

return this

方法详解

Java里的方法不能独立存在,必须定义在类里。方法逻辑上要么属于类,要么属于对象。

同一个类里不同方法直接相互调用时,如果被调用的是普通方法,默认使用this关键字作为调用者;如果被调用的是静态方法,则默认使用类作为调用者。

public class AnimalTest{
    public void person(){
        System.out.println("说话……");
        }
    public void animalT(){
        //person方法是普通方法,通过this关键字调用即可
        this.person();
        System.out.println("吃……");
        }
    public static void main(String[] args){
        AnimalTest AT = new AnimalTest();
        AT.animalT();
        }
    }

Java里方法的参数传递方式只有一种:值传递。所谓值传递,就是将实际参数值副本(复制品)传入方法内,而参数本身不会受到任何影响。

//下面的方法内可见main方法内a,b的值不会因为swap方法的执行而改变
public class SwapTest{
    public static void swap(int a, int b){
        int temp = a;
        a = b;
        b = temp;
        System.out.println("swap方法内: a = " + a + " b =" + b);
        }
    public static void main(String[] args){
        int a = 6;
        int b = 9;
        swap(a, b);
        System.out.println("main方法内: a = " + a + " b =" + b);
        }
    }

形参个数可变的方法

public class Vararge{
    public static void test(int a, String...books){
        //books被当成数组处理
        for(String book : books){
            System.out.println(book);
            }
        System.out.println(a);
        }
    public static void main(String[] args){
        test(3, "zed", "劫", "影流之主");
        }
    }

采用数组形参定义方法

public static void test(int a, String[] books)

这两种方法区别在于调用的时候存在差异,定义数组形参方法调用

test(3, String[]{ "zed", "劫", "影流之主"}

数组形式的形参可以处于形参列表的任意位置,但个数可变的形参只能处于形参列表的最后。也就是,一个方法中最多只能有一个个数可变的形参。

递归方法

一个方法体内调用它自身,被称为方法递归。方法递归包含一个隐式的循环,会重复执行某段代码,这种重复无须循环控制。

public class DiGuiTest{
    public static int fn(int a){
        if(a == 0){
            return 1;
            }
        else if(a == 1){
            return 4;
            }
        else{
            return 2 * fn(a - 1) + fn(a - 2);
            }
        }
    public static void main(String[] args){
        System.out.println(fn(10));
        }
    }
/*
    上面程序中fn(10)= 2 * fn(9) + fn(8);fn(9) = 2 * fn(8) + fn(7),依次类推,最后会到fn(2)=2 * fn(1) + fn(0)
    即fn(2)是可计算的,反算回去,得到fn(10)
*/

只要一个方法的方法体实现中再次调用了方法本身就是递归,递归是一定要向已知方向递归的。

方法的重载

Java允许一个类里定义多个同名方法,只是形参列表不同。一个类里方法名相同,形参列表不同,则被称为方法重载。

方法的重载有两个要求:方法名相同,参数列表不同。

public class OverLoad{
    //方法的重载
    public void test(){
        System.out.println("第一个方法");
        }
    public void test(int a){
        System.out.println("第二个方法");
        }
    public static void main(String[] args){
        OverLoad ol = new OverLoad();
        ol.test();
        ol.test(5);
        }
    }

其他的返回值类型、修饰符等和方法重载没有任何关系

public class OverLoad{
    //方法的重载
    public void test(){
        System.out.println("第一个方法");
        }
    //方法的重载方法名相同,形参列表不同,其他的返回值类型、修饰符和方法的重载无关
    private int test(int a, int b){
        return a + b;
        }
    public static void main(String[] args){
        OverLoad ol = new OverLoad();
        ol.test();
        System.out.println(ol.test(3, 4));
        }
    }

如果包含可变形参列表的重载,则需要注意:

public class OverLoadTest{
    public void test(String msg){
        System.out.println("只有一个字符串参数的test方法……");
        }
    public void test(String...books){
        System.out.println("N个字符串参数的test方法……");
        }
    public static void main(String[] args){
        OverLoadTest ol = new OverLoadTest();
        //调用第一个test方法
        ol.test("aa");
        //调用第二个方法
        ol.test("aa", "bb");
        //如果想一个字符调用第二个方法,传入字符串数组
        ol.test(new String[]{"aa"});
        }
    }

成员变量和局部变量

成员变量是在类里定义的变量,局部变量是在方法体内定义的变量。定义的时候建议每个单词首字母小写,之后的大写。

成员变量(实例变量,不以static修饰 以及类变量,以static修饰)

局部变量:形参、方法局部变量、代码块局部变量

如果通过一个实例修改类变量的值,其他实例访问也将获得一个被修改过的值。

public class NumTest{
    public static int a = 9;
    public static void main(String[] args){
        NumTest nt = new NumTest();
        //实例修改类变量
        nt.a = 0;
        System.out.println(a);
        }
    }

 

成员变量无须显示初始化,系统会在这个类准备阶段或创建该类的实例时进行默认初始化,成员变量默认初始化时的赋值规则与数组动态初始化时数组元素的赋值规则相同。

局部变量和成员变量不同,局部变量除了形参,都必须显式初始化,也就是先给方法局部变量和代码局部变量指定初始值,否则不能访问它们。

形参无须显示初始化,形参初始化在调用该方法时由系统完成,形参的值由方法的调用者指定。

一个类里不同定义同名的成员变量,即使一个类变量和一个实例变量也不行;一个方法内不同有同名的方法局部变量,方法局部变量不能和形参同名。同一方法内不同代码块的代码局部变量可以同名。如果先定义代码块局部变量,后定义方法局部变量,前面定义的代码块局部变量可以和后面定义的方法局部变量同名。

Java允许局部变量和成员变量同名,如果同名,局部变量会覆盖成员变量,如需调用被覆盖的成员变量,通过关键字this或者类名(类变量)作为调用者限定访问成员变量。

public class NumTest{
    public static int a = 9;
    public void test(){
        int a = 6;
        System.out.println("方法体内a的值为:" + a);
        System.out.println("类变量a的值为:" + this.a);
        }
    public static void main(String[] args){
        System.out.println("通过类名调用成员变量a: " + new NumTest().a);
        NumTest nt = new NumTest();
        nt.test();
        System.out.println("通过实例调用成员变量:" + nt.a);
        }
    }

局部变量保存在其方法的栈内存中,栈内存中的变量无须系统垃圾回收,往往岁方法或代码块的运行结束而结束。局部变量只保存基本类型的值或对象的引用,因此局部变量所占的内存区通常比较小。

成员变量的作用域扩大到类存在范围或者对象存在范围,这种范围有两种坏处:1、增大了变量的生存时间,这将导致更大的内存开销;2、扩大了变量的作用域,这不利于提高程序的内聚性。

隐藏和封装

面向对象三个特征:封装、继承和多态

封装:将对象的状态信息隐藏在对象内部,不允许外部程序直接访问对象内部信息,而是通过该类所提供的方法来实现对内部信息的操作和访问。

 作用域:

  当前(父,超)类: 同一Package: 派生(子)类: 其他Package:
public
protected x
default x x
private x x x

 

public: public表明该数据成员,成员函数是对所有用户开放的,所有用户都可以进行直接的访问。
private: private表示私有,私有的意思是除了class自己之外,任何人都不可以直接进行使用,私有财产神圣不可侵犯,即便是子女,朋友都不可以使用。
defualt: defualt表示友好,对于朋友来说,任何人都可以直接进行访问,对于子女或其他外部class,却是神圣的。
protected: protected对于子女,朋友来说,相当于public的,可以自由使用,无任何限制,而对于其他的外部class却是神圣的,不容别人侵犯。

 对于局部变量而言,其作用域就是就是其所在方法,不能被其他类访问,因此不能使用访问控制符来修饰。

关于访问控制符,存在如下几条原则:1、类里绝大部分成员变量应该使用private修饰,只有一些static修饰的、类似全局变量的成员变量,才可能考虑使用public修饰。有些方法只用于辅助该类的其他方法,这些方法被称为工具方法,工具方法用private修饰;2、如果某个类主要用作其他类的父类,该类包含的大部分方法可能仅希望被其他类重写,而不想被其他类调用,应该使用protected修饰这些方法;3、希望暴露被其他类自由调用使用public修饰。

Java常用包 《疯狂Java讲义》 138页

深入构造器

构造器是一个特殊的方法,这个特殊方法用于创建实例时执行初始化。

构造器最大的用处就是在创建对象时执行初始化,这种默认初始化把所有基本类型的实例变量设为0或false,把所有引用类型的实例变量设为null。

如果没有为Java类提供任何构造器,则系统会为这个类提供一个无参数的构造器,这个构造器执行体为空。

public class TTest{
    private String name;
    private int age;
    public TTest(String name, int age){
        this.age = age;
        this.name = name;
        }
    public static void main(String[] arge){
        //程序员自定义初始化
        TTest tt = new TTest("劫", 18);
        System.out.println(tt.name);
        System.out.println(tt.age);
        }
    }

当系统开始执行构造器的执行体之前,系统已经创建了一个对象,只是这个对象还不能被外部访问,只能在该构造器中通过this来引用。当构造器的执行体执行结束后,这个对象作为构造器的返回值被返回,通常还会赋予另一个引用类型的变量,从而让外部程序可以访问该对象。

构造器的重载和方法重载相似,要求构造器名字相同。为了让系统区分不同的构造器,多个构造器参数列表必须不同。

如果系统包含多个构造器,其中一个构造器执行体完全包含另一个构造器的执行体,可以通过this关键字调用相应的构造器。

public class TTest{
    private String name;
    private int age;
    private String color;
    public TTest(String name, int age){
        this.age = age;
        this.name = name;
        }
    public TTest(String name, int age, String color){
        //this关键字调用另一个构造器
        this(name, age);
        this.color = color;
        }
    public static void main(String[] arge){
        //程序员自定义初始化
        //TTest tt = new TTest("劫", 18);
        //System.out.println(tt.name);
        //System.out.println(tt.age);
        TTest tt = new TTest("akl", 16, "Green");
        System.out.println(tt.name);
        System.out.println(tt.age);
        System.out.println(tt.color);
        }
    }

类的继承

Java的继承通过extends关键字实现,实现继承的类被称为子类,被继承的类被称为父类,也被称为基类、超类。

子类是对父类的扩展,子类是一种特殊的父类。

Java不能获得父类的构造器。

Java类只能有一个父类。如果没有指定这个类的直接父类,默认扩展java.lang.Object类。java.lang.Object类是所有类的父类,要么是直接父类,要么是间接父类。

子类扩展了父类,父类派生了子类。

子类重写父类的方法:方法的重写需要遵守“两同两小一大”规则,两同:方法名相同,形参列表相同;两小:子类方法返回值类型等于小于父类的类型范围,子类的方法抛出的异常类应比父类抛出的异常类小或相等;一大:子类方法的访问权限应比父类方法的访问权限更大或相等。需要指出的是:覆盖方法和被覆盖方法要么是类方法,要么是实例方法,不能一个是类方法,一个是实例方法。

子类调用父类方法可以使用super(被覆盖的是实例方法)或父类类名(被覆盖的类方法)作为调用者类调用父类中被覆盖的方法。

如果父类方法具有private访问权限,则该方法对其子类是隐藏的,子类无法访问。

//子类无法访问父类private修饰的方法
private void test(){……}

无法访问也就无法重写了。

super是Java提供的一个关键字,super用于限定该对象调用它从父类得到实例变量或者方法。this不能出现在static修饰的方法中,super也不能。static修饰的方法是属于类的,该方法的调用者可能是一个类,而不是对象,因而,super限定也就失去了意义。

如果子类定义了和父类同名的变量,子类定义的变量会隐藏父类的变量,并不是完全覆盖。系统创建子类对象时,依然会为父类定义的、被隐藏的变量分配内存。

class  Extends{
    public String str = "均衡存乎暗影之中";
    }
public class ExtendsTest extends Extends{
    private String str = "均衡只是一个谎言";
    public static void main(String[] args){
        ExtendsTest et = new ExtendsTest();
        System.out.println(et.str);
        //显示向上转型,将输出父类中的信息
        System.out.println(((Extends)et).str);
        }
    }

通过super调用父类的构造器必须出现在子类构造器执行体的第一行,所以this和super不会同时出现。

子类构造器执行体既没有super调用,也没有this调用,父类构造器将会在子类构造器之前执行,隐式调用父类无参数构造器。

父类构造器总会在子类构造器之前执行,执行父类构造器时,系统会再次上溯其父类的构造器,所以最先执行的是java,lang.Object类的构造器。

如果某个父类通过this调用同类的构造器,就会依次执行此父类的多个构造器。

多态

Java引用变量有两个类型:一个是编译时类型,一个是运行时类型。

编译时类型由声明该变量时使用的类型决定,运行时类型由实际赋给变量的对象决定。

编译时和运行时类型不一样,就会出现多态。

class BaseClass{
    public int book = 6;
    public void base(){
        System.out.println("父类的普通方法");
        }
    public void test(){
        System.out.println("子类将会覆盖的父类方法");
        }
    }
class SubClass extends BaseClass{
    public String book = "zed";
    public void test(){
        System.out.println("子类覆盖父类方法");
        }
    public void Sub(){
        System.out.println("子类普通方法");
        }
    }
public class ClassTest{
    public static void main(String[] args){
        BaseClass bc = new SubClass();
        //输出父类book的值
        System.out.println(bc.book);
        //输出子类的test()方法
        bc.test();
        //输出父类的base方法
        bc.base();
        //父类没有Sub方法,所以下面出错
        //bc.Sub();
        }
    }

上面程序中test()方法,编译时类型是BaseClass,运行时是SubClass。

Java允许把一个子类对象直接赋给一个父类引用变量,无须任何类型转换,或者成为向上转型,向上转型系统自动完成。

相同类型变量、调用同一方法时呈现出多种不同的行为特征,这就是多态。

对象的实例变量不具备多态性。

试图把一个父类的实例转换为子类类型,则这个对象必须实际上是子类实例才行(即编译时是父类类型,运行时是子类类型),因此进行类型转换时应先通过instanceof运算符判断是否可以成功转换,从而避免出现ClassCastException异常。

子类转换为父类,称为向上转型,总是可以成功的。

instanceof运算符前面的操作数的编译时类型要么与后面的类相同,要么具有父子继承关系,不然编译错误。

instanceof运算符前面通常是一个引用类型变量,后一个操作数通常是一个类 (也可是 一个接口,把接口理解成一个特殊的类)。

注:在一些情况下并不是适合使用继承。比如:鸟、动物、狼,可以使鸟和狼继承动物,但是狼并不是由动物继承的,狼是动物中的一员,所以更适合使用组合。继承表达的是“is——a”的关系,而组合表达的是“has——a”的关系。

初始化块

初始化块与构造器功能类似,初始化块是Java类里可出现的第4种成员,一个类可以定义多个初始化块,相同类型的代码块直接有顺序。先定义先执行。

[修饰符] {
  //初始化块的可执行性代码  
}

 

初始化块的修饰符只能是static,使用static修饰的初始化块被称为静态初始化块。

初始化块可以包含任何可执行性语,包括定义局部变量、调用其他对象的方法,以及使用分支、循环语句等。

当创建一个Java对象时,系统总是先调用该类里定义的初始化块。

初始化块无法被调用,只是创建对象时隐式执行,而且是在执行构造器之前执行。

系统同样可以使用初始化块来进行对象的初始化操作。

如果两个构造器有相同的初始化代码,且无需接收参数,就可以把它们放在初始化块中定义。

普通初始化块负责对对象进行初始化,类初始化(静态初始化)负责对类进行初始化。

继承中初始化块的执行顺序:父类的静态初始化块——子类的静态初始化块;对象的初始化块(普通初始化块):先执行父类的——再执行子类的。同时有静态初始化和普通初始化块:父类静态初始化块——子类静态初始化块——父类普通初始化块——子类普通初始化块。

Java系统加载并初始化某个类是,总是保证父类(包括直接父类和间接父类)全部加载并初始化。

class Root{
    static{
        System.out.println("Root静态初始化");
        }
    {
        System.out.println("Root普通初始化");
        }
    }
class Mid extends Root{
    static{
        System.out.println("Mid静态初始化块");
        }
    {
        System.out.println("Mid普通初始化块");
        }
    }
public class Top extends Mid{
    static{
        System.out.println("Top静态初始化块");
        }
    {
        System.out.println("Top普通初始化块");
        }
    public static void main(String[] args){
        Top t = new Top();
        }
    }

 

 

 

 

 

面向对象

标签:

原文地址:http://www.cnblogs.com/changzuidaerguai/p/5659054.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!