Java基础 第二天

 

最近一直为找工作头疼,虽然说,我还有一年可以准备的,但依然压力很大。但压力就是动力,每到这个时候,我就会很努力.还是要声明一下,这个系列的文章,只是我复习的一个笔记,参考的是孙鑫老师的课程。今天大概的介绍一下面向对象编程吧。

一、面向对象编程概述

面向对象,英文呢,是OOP,也就是Object Oriented Programming。

 

用一句话来说

面向对象编程描述的一系列对象,以及对象之间的相互作用。而大家一开始接触的面向过程编程,描述的一些列函数,以及函数之间的相互作用。(这是我的理解)

面向对象编程与面向过程编程的区别

下面来具体看看有什么区别吧:

  • 前面已经提到,面向过程的是函数的集合,而面向对象是对象的集合,所以这算一个区别;
  • 有过面向过程编程经验的同学(还是以我为例吧,菜鸟的想法大家总是喜闻乐见的),肯定会遇到这样的情况,每当我们拿到一个问题,总是先去想解决这个问题的算法,再去确定数据结构是什么,比如拿到一个简单的题目,要计算一个长方形的面积,那么我就先想着去构造一个计算面积的函数,然后再去想想用什么数据结构来解决,这是面向过程编程的典型思路;而真正面向对象编程,我们应该先确定数据结构,再确定运算的过程,也即对同样的问题,我们首先应该想到去构造一个长方形的类,这个类(长方形)有哪些属性(长,宽等),有哪些方法(输出面积,输出长宽等),待这些都想明白了,再去考虑如何实现这些方法。这个区别也明显的吧。
  • 补充上面一段,对于面向过程编程,我们一般会构造一个数据结构来存储数据,然后另外定义函数(方法)来操作数据结构中的数据;而面向对象编程,通常我们看到的是一个个类,类是什么?是数据+对数据操作的方法的封装,所以对于面向过程编程,我们通常要构造一个对象模型(也就是类),这个类就将数据和方法组织在一起了。

二、类和对象

2.1 什么叫做类

在现实生活中,人就是对象,人类就是一个类,人与人在个体上是不同的,但是总的来说,是相似的,所以我们把相似的对象划归成一个类。

在软件中,类就是一个模板,它定义了通用于一个特定种类的所有对象的状态以及行为

说白了,类就是一个抽象模板,它是对一些列本质上相同的对象的描述,类中应该定义对象的状态信息(变量信息)和行为(方法)。以人为例,人的状态有哪些?是否睡觉,是否吃饭,身高信息,体重信息,年龄信息等,相关的方法就更多了。

2.2 什么叫做对象

对象是类的实例,这个对于初学者来说还是有点困惑吧,他们会问,什么是实例?

我想,实例就是利用这个类创建的一个东西。比如有一个类叫Person,那么,利用这句话,Person Mars = new Person();就创建了一个人的实例Mars,那么Mars就是对象啦。

三、面向对象程序设计

从程序设计的角度来看,在面向对象程序中,类是最基本的单元了(其实也不对,类中还有很多内容,但是从宏观来看的确是这样)。类实质上还是一种数据类型,只是相比较基本数据类型(如int),它比较特殊罢了,它是我们自己定义的数据类型。只要你是数据类型,那么我们就可以使用类名来声明一个变量(也可以说对象)。

在Java中,声明了一个变量,还不可以使用,必须再使用new运算符,去创建一个对象实体后,才可以使用这个对象。这是因为,声明只是在栈内存中有了一个名字,但是这个名字还没有对应的空间,需要使用new,在堆内存中申请一个空间,然后栈中的变量和堆中的空间对应上之后,这个对象才可以被使用。

3.1 类的构造方法

当我们用类构造一个对象的时候,我们一般会给这个对象初始化一系列属性,而构造函数就是用来初始化这些属性的。构造函数的名字很特别,与类名是相同的,并且没有返回值。

我们不能直接调用构造方法,必须通过new关键字来自动调用,从而创建类的实例。Java的类都要求有构造方法,如果没有定义构造方法,Java编译器会为我们提供一个缺省的构造方法,也就是不带参数的构造方法。

 

class Student{
    int num;//学号
    String name;//姓名
    /**
    *构造方法,初始化对象的属性
    */
    Student(){
        num = 0;
        name=null;
    }
    public static void main(String [] args){
        Student st = new Student();//ok
    }
}

其实我们也会发现,就算没有定义Student(),程序依然正确,这就是因为编译器提供了一个缺省的构造方法,系统用默认值初始化对象的成员变量。

我认为,我们有必要了解各种数据类型的默认值:

数值型 <--->  0

boolean <--->  false

char <--->  '\0’

对象 <--->  null

所以如果没有定义Student(),那么我们的变量还是会变为num=0,name=null。

还有一个内容要补充一下,就是new关键字:

上面我们已经使用了new,那么使用了它之后,会发生的事情我们也就知道啦:

为新生成的对象在堆内存中开辟一个空间,引起对象当中相应构造方法的调用,并且为对象返回一个引用,这样声明的对象就和开辟的空间联系起来了。

 

3.2方法的重载

重载的英文是overload,之所以要强调一下这个,是因为要区分方法重载和覆盖(override)的区别,覆盖在以后会讲到(3.5.2),现在我们先来讲一下重载。其实有了C++学习经验的同学肯定已经明白了。

重载构成的条件:方法的名称相同,但参数类型或参数个数不同,才能构成方法的重载。

什么时候需要用重载呢?举一个例子,不一定恰当,比如有一个方法来处理两个数相加的问题,这两个数有可能是int型的,那么我可以设置这个方法为void add(int a,int b),同样还有可能是两个double类型的数相加,为了让语义连贯,我们还应该使用add来处理,所以这个时候可以用到重载,void add(double a,double b)。

其实构造函数也可以重载,有时候构造函数我们需要传递一个参数进来,这就是重载啦。

举个例子:

class Cal{
    int a;
    int b;
    Cal(){
        a = 0;
        b = 0;
    }
    //带参数的构造函数
    Cal(int a,int b){
        this.a = a;
        this.b = b;
    }
    int add(){
        return this.a+this.b;
    }
    //add的重载
    int add(int a,int b){
        return a+b;
    }
    public static void main(String []args){
        Cal t = new Cal();
        System.out.println(t.add());//0
        System.out.println(t.add(1,1));//1
    }
}

但是,要注意,如果参数相同,而返回类型不同,这样可以吗?答案是显然的,不可以!所以一定要注意,一定是参数类型或者参数个数不相同。

3.3 特殊变量this

上一节中,我们看到一个Cal的构造方法中用到了this,这也是一个挺有意思的东西,下面就简单介绍一下:

this顾名思义,代表对象本身,记住代表的是一个对象的自身。

当类中有两个同名变量,一个属于类(比如Cal中的成员变量a),而另一个属于某个特定的方法(如构造方法中的局部变量a),可以使用this区分成员变量和局部变量。

在Cal(int a,int b)的函数体(作用域就在函数体内),其实已经屏蔽了外面的成员变量a和b。也就是说如果在函数体内这样写,a=a,b=b,那么就是自己给自己赋值,这显然不是我们想要的,于是就要通过this来帮助,this.a就是告诉编译器,这个a是属于我类的,而在这个函数体内(注意只是在这个函数体内),不加this的a是属于这个函数的。

下面我们来想一个问题,一个类的所有对象(类的实例化)在内存中是怎么样的呢?

这些对象调用的成员方法再内存中只有一份拷贝,尽管在内存中有可能有多个对象,但是方法再内存中只有一个拷贝,数据成员则好一些,在类的每一个对象所在的内存空间中都存在着一份拷贝。this变量允许相同的实例方法为不同的对象工作,举个例子来说,无论Cal有多少个对象,在内存中,方法add()只有一份拷贝,那么每当我们调用一个实例方法,比如Cal的对象t调用add()时,this变量便被设置成当前使用的对象,方法的代码(也就是add())将与this所代表的对象的数据进行关联。

3.4 static和final

 

3.4.1 static

static顾名思义(额..我老喜欢这样了)是静态的意思,啥是静态呢?也就是说不收外界动的影响,它是不依赖该类的特定实例的,被所有实例共享。当然了,这只是粗的理解,下面我来具体看看。

我们在哪些地方会看到或者说用到它。我们在看代码的或者写代码的时候经常会在一些变量或者一些方法(函数)的声明前面看到这个关键字,不如static int 后面跟上一个什么东西。

当static放在一个变量前面的话,那么这个变量就成为静态变量(如果前面木有static那么就叫做实例变量),如果它放在一个方法前面,那么这个方法就是静态方法(对应的有实例方法)。

当一个类被加载了,那么相关的静态变量与方法就会被加载的内存的一个固定位置,所以调用这些东西根本不需要实例化类的对象。

我们要注意一点,在静态方法中,也就是在用static声明的方法中,不能调用非静态的方法,也不能引用非静态的成员变量。反之,则可以。大家要想一想这是为什么?

本大菜鸟猜大家肯定懒得想了,看了这么长的文章早该昏昏欲睡了吧,所以就直接说吧。

上面提到,调用静态方法或者静态变量的时候,根本不需要实例化对象,只要类被加载进来就行了。所以,如果在静态方法里调用了非静态的内容,那么这些内容还没被分配空间,根本就无法使用啊,所以这是不可以的。

那么,能不能用类实例化的对象去调用静态方法呢?显然是可以的。。

由于静态变量是属于某一个类的(所有对象共享),因此它的改变在程序的生命周期内是持久的,我们可以利用这个来计数。(注意:只能在非分布式程序的情况下,如果在分布式的情况下应该想想别的办法了)

对了,还有一个重要的内容:

**如果一个类文件中有main,并且在这个文件中定义了一个内部类,那么一定要记得把这个类声明成static的,才能在main中使用。

3.4.2 final

final是用来定义常量的,例如:final double PI = 3.1415;

之所以PI要大小,是因为我们通常约定在定义常量的时候,采用大写的形式。

下面,有一个很重要的问题,常量在什么时候赋值呢?

final常量可以在声明的同时赋初值,也可以在构造函数中赋初值(注意:是初值,也就是之前并没被赋值)。

当一个变量被定义为常量,那么这个变量的值以后都不会被改变,也就是说,无论定义了多少个对象,这个对象的成员变量PI都为3.1415不会为其他的,这个时候我们想,成员变量在类的每一个对象所在的内存空间中都存在着一份拷贝,而这个常量的值是不会被改变,所以我们浪费了内存,那么有什么办法节省内存呢?答案就是利用static。但是要注意,在用static声明之后,就要立即初始化了。

为了节省内存,我们通常将常量声明为静态的。

3.5 类的继承

3.5.1 类的继承概念

在Java中,通过关键字extends继承一个已有的类,被继承的类称为父类(超类,基类),新的类称为子类(派生类)。要注意,在Java中,不允许多继承

首先来解释一下为什么要继承?继承为我们的开发提供了很大的便利,通过继承,我们可以很方便的获得父类的方法。对了,刚刚提到在Java中不允许多继承,那么难道就没有办法了吗?错,其实我们还可以利用接口,以后我们会介绍。

嗯,前面提到,通过继承,我们可以使用父类的方法,有些时候,父类的方法不能满足我们,比如说,父类是一个图形,子类是一个三角形,那么三角形的画法肯定跟普通图形的画法不一样,所以我们要重新修改一下这个方法。

我们不需要专门去修改父类的方法,因为这样会影响到别的子类的使用(比如圆,矩形等),这里就要介绍一个新知识了,方法的覆盖(override),注意与3.2节做一个比较。

3.5.2 方法的覆盖

在子类中定义一个与父类同名、返回类型、参数类型均相同一个方法,称为方法的覆盖。注意:覆盖发生在子类与父类之间。

举个例子:

class Animal{
    void run{
        System.out.println("animal run");
    }
}
class Fish extends Animal{
    void run{
        System.out.println("fish swim);
    }
}

3.5.3 super

super提供了对父类的访问,可以使用super访问父类被子类隐藏的变量或者覆盖的方法。

每个子类构造方法的第一条语句,都是隐含地调用super(),如果父类没有这种形式的构造函数,那么在编译的时候就会报错。但是如果指定了super(参数);就不会再调用super()了。

还要注意一点,super(),注意是加括号的,只能放在构造函数中,无论括号里面有木有参数,都只能放在构造函数中,并且只能放在第一句,这很重要。为什么要放在第一句?这是因为默认第一句就是一个super()虽然是隐藏的。如果你不写在第一句,那么就已经调用了父类的构造函数了,你后面又来一次,就会出现混乱。

另外,在子类覆盖了父类的方法中,不会自动调用父类方法中的内容,如果想要使用父类方法中的内容,需要这样:super.相应的方法,来使用父类中的代码。

这里正好可以解释一个现象,就是子类被实例化的时候,总是先调用父类的不带参数的构造函数。

class Test{

    Test(){
        System.out.println("父类的构造函数:不带参数");
    }
    Test(String str){
        System.out.println("父类的构造函数:带参数="+str);
    }
    void print(){
        System.out.println("父类的输出方法");
    }
    
    public static void main(String []args){
        Test1 t = new Test1("s");
        /*
         * 输出为:
         * 父类的构造函数:带参数=s
         * 子类的构造函数:带参数=s
         */
        t.print();
        /*
         * 子类的输出方法
         * 父类的输出方法
         */
        /////////////////////
        /*
        Test1 t1 = new Test1();
        /*
         * 输出为:
         * 父类的构造函数:不带参数
         * 子类的构造函数:不带参数
         */
        
    }
}
class Test1 extends Test{
    Test1(){
        System.out.println("子类的构造函数:不带参数");
    }
    Test1(String str){
        super(str);//必须放在第一句,不然会报错,如果没有这句话,则输出的是,父类的构造函数:不带参数
        System.out.println("子类的构造函数:带参数="+str);
        
    }
    void print(){
        System.out.println("子类的输出方法");
        super.print();//此时可不放在第一句了
    }
}

3.5.3 多态性

多态性正是面向对象编程灵活的地方。利用多态性,我们可以通过覆盖父类的方法来实现,在运行时根据传递的对象引用,来调用相应的方法。

举个例子:

class Test{

    void print(){
        System.out.println("Test");
    }
    public static void main(String []args){
        Test t = new Test();
        t.print();
        Test1 t1 = new Test1();
        Test2 t2 = new Test2();
        t = t1;
        t.print();
        t = t2;
        t.print();

        if(t instanceof Test2){
            System.out.println("t当前是Test2的实例");
        }
        if(t instanceof Test){
            System.out.println("t当前是Test的实例");
        }
        if(t1 instanceof Test){
            System.out.println("t1当前是Test的实例");
        }
    }
}
class Test1 extends Test{
    void print(){
        System.out.println("Test1");
    }
}
class Test2 extends Test{
    void print(){
        System.out.println("Test2");
    }
}
/*
* 输出:
* Test
* Test1
* Test2
* t当前是Test2的实例
* t当前是Test的实例
* t1当前是Test的实例
*/

其中有一个instanceof,这是一个非常非常有用的东西啊,通过它可以判断当前对象是哪一个类的实例,一定要会用它,我曾帮师兄做过一个编辑器,利用GEF做的,经常需要判断当前的模型属于哪个具体的类,就要用到instanceof,实在是太方便了。

我们还可以看到,子类一定是父类的实例哦。

四、小结

今天主要介绍了面向对象的基本知识,真的是非常的基本了,不然一篇文章根本说不完。

下面是…吐槽时间

最近被老板安排做一些非常繁琐且对长姿势没用的工作,心里很是郁闷啊,不过看老板也忙的不得了,能分担点就分担点吧。写这个博客其实也蛮浪费的时间的,但是可以让我静下心来,还是挺好的。不过,现在写这些东西,估计已经没人看了吧,如果有同学再看,希望不要误导你,这个只是我自己笔记,其实也算不上笔记,都是自己回想当时看的内容写的,加上自己的一些体会。

要期末考试了,需要攒人品。OMG,下个星期还要汇报,好烦啊。。

OK,今天到这里吧,过段时间会写一些关于GEF的东西,下次见咯~

(文章地址:http://www.cnblogs.com/njuduyu/archive/2013/05/24/3097531.html

posted @ 2013-05-24 20:17  胖叔  阅读(268)  评论(0)    收藏  举报