Java学习笔记(2)

来源:互联网 发布:mac os 光盘镜像 编辑:程序博客网 时间:2024/04/28 20:23

对象交互

当一个对象里有多个对象的时候,那些对象之间是如何交互的,对象和对象之间的联系是如何建立的,对象如何和其他对象交流。对象和对象之间的联系紧密程度叫做耦合。对象和对象的耦合程度越紧,表现在源代码上,就是它们的代码是互相依赖、互相牵制的。我们理想的模型,是对象和对象之间的耦合要尽可能的松,平行的对象要尽量减少直接联系,让更高层次的对象来提供通信服务。


封装

封装,就是把数据和对这些数据的操作放在一起,并且用这些操作把数据掩盖起来,是面向对象的基本概念之一,也是最核心的概念。

我们有一个非常直截了当的手段来保证在类的设计的时候做到封装:
所有的成员变量必须是private的,这样就避免别人任意使用你的内部数据;
所有public的函数,只是用来实现这个类的对象或类自己要提供的服务的,而不是用来直接访问数据的。除非对数据的访问就是这个类及对象的服务。简单地说,给每个成员变量提供一对用于读写的get/set函数也是不合适的设计。

同一个类的不同对象之间可以访问对方的私有的成员变量或调用对方的private成员方法。

java里面,如果一个成员函数前面没有定义是public还是private的,我们称之为frinedly,意思是,和它位于同一个包的其他类可以访问它。其他包里的类就不能访问啦。

标为public的类必须定义在它自己的文件里面,就是说类名和文件名必须统一

一个.java文件是一个编译单元compilation unit

一个compilation unit里面可以有很多类,但只有一个可以是public的

如果一个类不是public的,它只能在这个包里面起作用,不能拿出去用


Package

当你的程序越来越大的时候,你就会需要有一个机制帮助你管理一个工程中众多的类了。包就是Java的类库管理机制,它借助文件系统的目录来管理类库,一个包就是一个目录,一个包内的所有的类必须放在一个目录下,那个目录的名字必须是包的名字。


类变量

类是描述,对象是实体。在类里所描述的成员变量,是位于这个类的每一个对象中的。
而如果某个成员有static关键字做修饰,它就不再属于每一个对象,而是属于整个类的了。(类变量)

通过每个对象都可以访问到这些类变量和类函数,但是也可以通过类的名字来访问它们。类函数由于不属于任何对象,因此也没有办法建立与调用它们的对象的关系,就不能访问任何非static的成员变量和成员函数了。


容器

容器是现代程序设计非常基础而重要的手段。

所谓容器,就是“放东西的东西”。数组可以看作是一种容器,但是数组的元素个数一旦确定就无法改变,这在实际使用中是很大的不足。一般意义上的容器,是指具有自动增长容量能力的存放数据的一种数据结构。在面向对象语言中,这种数据结构本身表达为一个对象。所以才有“放东西的东西”的说法。

Java具有丰富的容器,Java的容器具有丰富的功能和良好的性能。熟悉并能充分有效地利用好容器,是现代程序设计的基本能力。

我们首先学习的是顺序容器,即放进容器中的对象是按照指定的顺序(放的顺序)排列起来的,而且允许具有相同值的多个对象存在。


顺序容器

(编程时要实现的一个目标:要让人机交互和业务逻辑分离)

范型类容器

private ArrayList<String> notes = new ArrayList<String>();
容器类有两个类型:

  • 容器的类型(ArrayList)
  • 元素的类型(<String>)

(要好好看手册,弄清楚系统类的方法们,它能帮你做什么事情)

Q:当我们用add函数把对象放进容器中去的时候,究竟是把什么放进了容器?放进去的是对象本身吗?放进去以后,那个对象还在外面吗?如果修改了放进去的那个对象,容器中的对象会变化吗?

容器中存的是对象的引用或者说是指针地址,容器中的对象随着外部对其的操作而改变。


对象数组

当数组的元素的类型是类的时候,数组的每一个元素其实只是对象的管理者而不是对象本身。因此,仅仅创建数组并没有创建其中的每一个对象!


上图的结果是,ia[0]输出0,a[0]输出null。因为现在这个东西其实还不存在



对容器类来说,for each循环也是可以用的。

String[] list = new String[];//...for(String s : list){//...}

集合容器

集合就是数学中的集合的概念:所有的元素都具有唯一的值,元素在其中没有顺序。

HashSet<String> s = new HashSet<String>();System.out.println(s);

只要我们在java的类里面实现了下面这个函数(函数体里面的东西自己定)

public String toString() {return "" + i; }

就可以用System.out.println(s);直接把这个类的一个对象的值给输出。java会自动去调用toString()这个函数,输出的值就是返回的String类型的东西


散列表(Hash)

传统意义上的Hash表,是能以int做值,将数据存放起来的数据结构。Java的Hash表可以以任何实现了hash()函数的类的对象做值来存放对象。
Hash表是非常有用的数据结构,熟悉它,充分使用它,往往能起到事半功倍的效果。


对hash表来说,key是唯一的


继承

基于已有的设计创造新的设计,就是面向对象程序设计中的继承。在继承中,新的类不是凭空产生的,而是基于一个已经存在的类而定义出来的。通过继承,新的类自动获得了基础类中所有的成员,包括成员变量和方法,包括各种访问属性的成员,无论是public还是private。当然,在这之后,程序员还可以加入自己的新的成员,包括变量和方法。显然,通过继承来定义新的类,远比从头开始写一个新的类要简单快捷和方便。继承是支持代码重用的重要手段之一。

class ThisClass extends SuperClass {     //…}

Java的继承只允许单继承,即一个类只能有一个父类。

对理解继承来说,最重要的事情是,知道哪些东西被继承了,或者说,子类从父类那里得到了什么。答案是:所有的东西,所有的父类的成员,包括变量和方法,都成为了子类的成员,除了构造方法。构造方法是父类所独有的,因为它们的名字就是类的名字,所以父类的构造方法在子类中不存在。除此之外,子类继承得到了父类所有的成员。
但是得到不等于可以随便使用。每个成员有不同的访问属性,子类继承得到了父类所有的成员,但是不同的访问属性使得子类在使用这些成员时有所不同:有些父类的成员直接成为子类的对外的界面,有些则被深深地隐藏起来,即使子类自己也不能直接访问。下表列出了不同访问属性的父类成员在子类中的访问属性:


我们不可以在子类中重新定义继承得到的成员的访问属性。如果我们试图重新定义一个在父类中已经存在的成员变量,那么我们是在定义一个与父类的成员变量完全无关的变量,在子类中我们可以访问这个定义在子类中的变量,在父类的方法中访问父类的那个。尽管它们同名但是互不影响。

在构造一个子类的对象时,父类的构造方法也是会被调用的,而且父类的构造方法在子类的构造方法之前被调用。在程序运行过程中,子类对象的一部分空间存放的是父类对象。因为子类从父类得到继承,在子类对象初始化过程中可能会使用到父类的成员。所以父类的空间正是要先被初始化的,然后子类的空间才得到初始化。在这个过程中,如果父类的构造方法需要参数,如何传递参数就很重要了。

在构造函数里面调用,super()只能用一次,也只能在第一行

如果子类和父类有一个同名的函数,子类想要调用父类的那个函数,则必须在函数名字前面加一个super

如:

suoer.print();

父类的私有成员,子类拥有,但不可直接访问,可以通过父类方法使用父类成员变量,子类再调用父类的这个方法来使用


多态变量和向上造型

子类的对象可以被当做父类的对象来使用,如:

  • 赋值给父类的变量
  • 传递给需要父类对象的函数
  • 放进存放父类对象的容器里




一个变量可以保存其所声明的类型或该类型的任何子类型。
对象变量可以保存其声明的类型的对象,或该类型的任何子类型的对象。
Java中保存对象类型的变量是多态变量。“多态”这个术语(字面意思是许多形态)是指一个变量可以保存不同类型(即其声明的类型或任何子类型)的对象。


多态变量

  • Java的对象变量是多态的,它们能保存不止一种类型的对象
  • 它们可以保持的是声明类型的对象,或声明类型的子类的对象
  • 当把子类的对象赋给父类的变量的时候,就发生了向上造型

造型:把一个类型的对象赋给另一个类型的变量


在Java里面,对象变量的赋值并不是拿一个对象去给另一个对象赋值,而是让这两个对象的管理者去管同一个对象。

大多数oop语言都是这样的,除了c++(在c++里面可以做两个对象的赋值)

赋值运算符(=)做的事情就是,让这个变量去管理(指向了)另一个对象。而不是把另一个对象对这个对象进行赋值。

造型和类型转换是不同的。

int i = (int)10.2; //这个是类型转换。因为10.2真的被转换成int的10了

而造型是,我把它当成另一个类型的对象来看待,而不是真的把它转换为另一个类型


向上造型:

  • 拿一个子类的对象,当作父类的对象来用
  • 向上造型是默认的,不需要运算符
  • 向上造型总是安全的


多态

如果子类的方法覆盖了父类的方法,我们也说父类的那个方法在子类有了新的版本或者新的实现。覆盖的新版本具有与老版本相同的方法签名:相同的方法名称和参数表。因此,对于外界来说,子类并没有增加新的方法,仍然是在父类中定义过的那个方法。不同的是,这是一个新版本,所以通过子类的对象调用这个方法,执行的是子类自己的方法。
覆盖关系并不说明父类中的方法已经不存在了,而是当通过一个子类的对象调用这个方法时,子类中的方法取代了父类的方法,父类的这个方法被“覆盖”起来而看不见了。而当通过父类的对象调用这个方法时,实际上执行的仍然是父类中的这个方法。注意我们这里说的是对象而不是变量,因为一个类型为父类的变量有可能实际指向的是一个子类的对象。

绑定

当调用一个方法时,究竟应该调用哪个方法,这件事情叫做绑定。绑定表明了调用一个方法的时候,我们使用的是哪个方法。

  • 早绑定,又称静态绑定,这种绑定在编译的时候就确定了;
  • 晚绑定,即动态绑定。动态绑定在运行的时候根据变量当时实际所指的对象的类型动态决定调用的方法。

Java缺省使用动态绑定。


覆盖override

  • 子类和父类存在名称和参数表完全相同的函数,这一对函数构成覆盖关系
  • 通过父类的变量调用存在覆盖关系的函数时,会调用变量当时所管理的对象所属的类的函数


类型系统

OBJECT类

Java中所有的类都是Object类的子类。它是root 。单根结构。(除了c++)

Object类的函数

  • toString()
  • equals()


2017.1.19

0 0
原创粉丝点击