摘要:这篇笔记主要记录了学习《Java核心技术 卷一》的第四章时的一些心得,主要阐述了对象与类这一部分的内容。需要注意的是,这一章的内容需要精心学习,因为很多知识点在笔试面试中很容易考到。
你有对象吗?没有的话,我们可以new一个,现实生活中,对象可能很贫瘠,但是在面向对象的编程语言中,我们有着丰沛的对象,我们每天与之打交道的,就是对象。
对于大部分科班出身的计算机系学生,可能接受面向对象这一概念更难一些,因为他们大部分在一开始学习的是C语言,有些可能走的更远一些,学习过C++,接触过C++对象,但是说起面向对象,仍然是浑浊不清。这是因为,C语言是面向过程的编程语言,C语言的一段程序,完全是在描述一个解决问题的过程,对于C语言而言,解决一个问题,就类似于我们人类一点一点的自己发出指令,让系统完成需要完成的事情,而对于面向对象的编程语言,我们面对的不再是过程,而是一个一个的拥有自己行为的,拥有自己特性的组件,我们不再从底层的解决过程发号施令,而是对这些组件发号施令,这些组件相互作用,完成我们的任务。当我们写面向过程的程序时间长了,自然对于面向对象的思想很难一时接受,现在就让我们深入理解面向对象的含义。
首先我们需要知道的一点是:面向对象不是Java语言的特性,而是一种广泛被认可的编程思想,在很多编程语言中都会运用到这个思想,并且如今大部分的高级语言都支持面向对象的编程。Python,C#,Go等语言均支持面向对象的编程,Java只不过是其中普通的一种。
那什么是面向对象呢?面向对象实际上是一种设计程序的思路,现在我们先回想一下C语言中的一个东西:结构体,结构体这东西,是一个将多个属性包装成一个大属性的功能,比如我们将身高,体重,年龄,性别属性包装成一个人类属性,如下:
typedef struct{ int height; int weight; int age; int gender; } Person;
这样一来,我们就可以直接声明一个Person类型的变量,然后存储这四个属性,每一个变量实体就代表了一个人类。其实在这里,C语言已经有了面向对象的雏形了。有了结构体之后,我们通常会为结构体进行相关方法的配套设计,我们会为这个结构体类型进行一系列的函数设计,进而表示结构体可以执行的函数,或者说,人类可以干的事情,说到这里,我们就可以引出对象这个概念了。
以上的C语言结构体操作已经很像对象了,但是它仍然不是对象,这要和对象的含义说起。什么是对象?对象的英文是object,我们使用百度翻译来搜索一下:
除对象之外,它还有物体,物品,东西的意思,了解了这些含义,我们就能够更好的理解对象了,所谓对象,实际上就是一个物品,一个东西。我是一个东西,你也是一个东西,汽车是一个东西,飞机也是,太阳是一个东西,巨引源也是一个东西,拉尼亚凯亚超星系团是一个东西,宇宙也是一个东西,万物都是东西,而一个东西,有什么特征呢?比如我的笔记本,它就是一个东西,我们如何准确的定义我的笔记本?首先,它有一个产品序列号;其次,它有一个产品型号;然后,它是戴尔的;还有,它是黑色塑料外壳的;同时,它装有酷睿i5的8系CPU;并且,它具备12g的内存…以上这些都是它的特征,然而除了特征之外,这个笔记本还有一项重要的指标,那就是它的功能:首先,它可以开机;其次,它可以打字;再者,它可以放歌;还有,它可以直播…这些都是它的功能。我们最终发现,我们定义一个特定的物品,通常都是这样:这是一个…样的,可以干…的东西。比如一个花牛苹果:这是一个深红色的、花了我8块钱买来的、种类叫花牛的,可以用来吃的,还可以用来送人的花牛苹果。
所以我们经过最终的精简,发现了事件万物的基本定义法,就是特征+行为。包括我们定义动物,也是从各种直接观察得到的特征以及它的行为来进行定义,特征一样但是行为不同,这就是不同的两个东西,比如在水里游动的鲨鱼和在陆地上行走的鲨鱼,这两种我们认定肯定不是一个东西。所以,我们认为对象,也就一个东西,必须具备自己的属性,和行为,对于行为,比较显得文雅的叫法就是方法。在编程语言中,采取仿生的方式,将一个在现实世界中存在的东西模拟为一个计算机中的数据,将这个东西的属性以及行为封装进一个单位内进行保存的方式,就叫面向对象。面向对象就是对一个事物的特征以及行为进行抽象并模仿现实,将它们封装进一个单位。这种方式更加符合现实中的真实状况,因此在使用起来更加的方便,因此广受欢迎。在面向对象的编程语言中,想要使用一个对象的方法,必须有这个对象,然后通过这个对象调用这个方法,这就像在现实生活中,我们要让一个小亮表演一次后空翻,就首先要找到小亮,然后对他说:“小亮,给他整个活!”
在C语言中,我们可不是根据某一个结构体来调用函数的,函数想用就直接用,在C语言中,函数是主角,一个结构体变量只是它的参数,只是它的绿叶,一个函数是否调用不取决于某个东西是否存在,我们可以随意调用这个函数,并且形参我们甚至可以传进去一些其他类型的变量,可能也不会报错。在一个对象中封装进属性以及方法,在使用时以对象为主体,使用对象才能调用各种方法,获取各种属性,正如在现实生活中处理问题一样的程序设计思想,叫做面向对象的编程方式;以过程为主体,不以个体为中心,而是以功能为核心,使用功能操纵个体,且不存在类似显示生活中这种个体封装属性和方法的编程思想,叫做面向过程的编程。
尽管如此,我们很难界定哪个好哪个不好,因为即使在面向对象的编程中,在一个方法的实现中,我们仍然会使用到面向过程的编程思想,只不过是看着哪合适,就用那种思想罢了。
什么是类?什么是对象?类和对象的关系,仍然是来自于现实生活中的概念模拟。在现实生活中,总有一些东西,具备相同的特征,比如咱们,都是一个脑袋,两个胳膊两个腿,直立行走,体表毛发很少,脑容量大,一看就和路边翻垃圾的四脚着地,有一条尾巴,只会发出汪汪叫的东西不一样,这样,我们中的一个聪明的对象就提出了分类思想:我们这些具备相似特征的东西们是一类,那些具备另一种相似特征的东西们是一类,最后,我们便发明了一种分类规则,即把某些具备相似特征的对象们总体上称之为一类,为它们划分界限。
现在我们可以知道:分类就是根据对象的特征和行为方式,进行更加抽象的,笼统的划分,将一些具备同样的特征和行为的东西划分为一类。如人类,狗类,鱼类,而人类中的你我,狗类中的大黄与旺财,鱼类中的我今天中午吃的那一条黄花鱼,便都是类中的一个独特的个体,称之为对象。Java中的类便和这个类是一个意思,具有相同属性,相同方法的对象,便都属于一个类。在编程语言中,我们则更像上帝,我们先定义一个类,也就是先定义一个对象群体的模板,然后再根据这个模板大量创建对象,这些对象具备相同的属性,只不过可能因为属性名不同而有所差别。如此说来,女娲造人的过程,就是在根据一个人类模板不停的new人类对象,一开始祂使用捏泥人的方式,就类似面向过程的编程,而之后定义了人类的特性,便开始用柳条甩泥点子(这个过程未免草率,但是我们仍然可以理解为她为泥点子加入了模板控制程序)甩人出来,效率明显快了很多。
由此可见,类就是对象的模板,类是对象的蓝图,类具备对象们的基本特征,根据类我们来具体定义一个对象。对象是类的实例,对象具备类中定义的特征,只不过是因为对象和对象之间的特征值不同,导致了对象之间的差异。关于特征与特征值,其实也是一个仿生,对象中的属性和属性值,js中传递信息的键值对,实际上也都是仿生了现实生活中的属性和属性名,如你我都有姓名,你我都有性别,姓名和性别就是我们作为人的特征,而我叫X,你叫Y,我是男的,你是女的,这些就是特征值,而在面向对象编程中,便仿生了这一概念,对象有自己的属性,也有自己的属性值。你我因具备共同的特征而属于一类,但是我们因特征值不同而不同。对象因具备同样的属性而同属一类,而因为属性值不同而区分开来。在我们制造电脑时:冯诺依曼结构以及目前硬件协议就是类,而生产出来的不同的电脑就是不同的不同的对象。
因此我们知道,面向对象编程实际上是对生活中的分类概念的模拟,在计算机中,也将具备同样特征和行为的个体们封装成类,然后制造出特征值不同的实例来进行使用。这种行为方式更符合我们人类的行为方式,因此大受欢迎。
我们通常使用如下代码进行类的声明:
class Person{ public String name;//公共属性 public int age;//公共属性 public int height;//公共属性 private int weight;//公共属性 private boolean gender;//公共属性 public void sayHello(){ System.out.println("Hello!My name is" + name); } public int returnMyAge(){ return age; } return int myWeight(){ int theWeightOfCloth = 15; return this.weight - theWeightOfCloth; } return int myHeight(){ int height = 10; return this.height + height; } }
如代码所示就是一个简单的类的声明,类的声明通常是:class 类名{}。其中在class的前边,可以加public标识符,以标示这个类是主类,如果想在一个类中写主方法,那么这个类一定要是主类,一个java文件中只能有一个主类,其余类不能加public标识符。实际上在我们编写java源文件时,一个源文件中只能有一个类被public修饰,这个类将被识别为主类,这个类必须和源文件名同名,其余类的默认修饰符为protected。被public修饰的类对外开放,可以跨包调用,可以被其他包中的类继承,调用。没有被修饰的类只能被同包中的类调用继承。Java是一种存活与类中的编程语言,在被编译时,一个源代码中的所有类会被编译为独立的类文件,也就是.class文件。
在上面的代码中,方法外边定义的变量被称为公共变量,它们和其他的变量在含以上存在区别,它们是这个类的属性,而不是普通的变量,在类内的任何地方都可以调用到这个变量,作为属性的他们需要被修饰符进行修饰,被public修饰的变量可以直接使用对象调用,而被private修饰的变量则不行,需要使用访问器和修改器进行访问或者修改,我们使用访问器可以对访问器做点手脚,如在上面代码中的myWeight方法就是一个体重属性的访问器,我们可以在里边加些手脚,进而在有需求时,如需要保证数据安全加密数据时安全的使用属性值。
在方法中定义的变量被称为局部变量,它们就是普通的变量,在方法的内部往往使用到的是面向过程的编程思想,这些变量便不具备其他特殊意义,只作为一个变量存在。局部变量只能在方法中被调用。在方法之外就用不了了。
在Java中,存在八种基本类型,在声明新变量时我们需要在前边声明类型,这是为了让系统开辟出相应的空间来存储这些变量。**在计算机底层,不同的数据类型有着不同的存储结构,不同的类型会告诉系统如何开辟存储空间,开辟什么样的存储空间。它会告诉你在内存中如何开辟空间。**在声明一个对象类型的变量前,我们也要在最前边声明变量的类型,最前边的对象类型声明也是在告诉系统开辟什么样的,多大的存储空间。一个对象类型的存储空间的声明如下:
Cat cat1 = new Cat();
在上边这个语句中,最前边的Cat是变量类型声明,cat1是变量名,new关键字的意思是在堆上创建对象,而Cat()就是构造器了,构造器的作用是在new返回一个对象地址之前初始化这个对象,关于变量的声明以及对象类型变量的声明,在这里我进行了深入的探讨,在此我们先探究构造器。
在Java中,方法是可以重写的,我们可以在一个类中定义多个同名方法,主要它们的方法签名不冲突,它们就可以共存,系统就认定它们是不同的方法,那么什么是方法签名?在现实生活中,也存在签名这一概念,对于明星们而言,他们往往有着与众不同的签名,狂热的粉丝通常能够认出自己偶像的签名,如果在现实生活中,出现了两个叫二次元刀酱的明星,那么这两个人为了恰双份的烂钱,必定会有不一样的签名,否则就重了。对于方法也是一样,只要能够让系统区分出来,即使同名,也是允许的。
一个方法的方法签名包括:方法名、参数列表。方法名大家都知道,参数列表就是这个方法的参数具体情况,如类型以及数量等。一般来说这里的参数列表指的是方法的显式参数,也就是方法名后边的括号里边的那些参数,实际上隐式参数也可以用于方法的区分,然而这是废话,隐式参数通常是一个类,不同的类中含有同名方法是完全没有问题的,在这里方法签名仅针对一个类中的同名方法,因此我们记住:方法签名包含方法名和参数列表,参数列表具体指的是形参的具体状况。
构造器的特点:
1.构造器的名称和类的名称相同。
2.构造器没有返回值,没有返回值类型这个关键字部分。
3.构造器的最主要特点是初始化对象,在创建对象的时候执行其内部的程序,进而对这个对象进行初始化。
4.在一个类中有一个或者可以存在一个以上的不同的构造器。
5.在一个类当中有一个默认的,不显示的,无参构造器,这是最基本的构造器。且这个构造器很容易失效,当我们在这个类当中重写一个构造器的时候,这个默认的构造器将会被覆盖。被覆盖后就彻底不能用了。
其中第五条非常重要,如果我们对对象的初始化没有要求时,就没必要书写构造器,但是即使不需要初始化,类中也需要有一个象征性的占位用的构造器,这个构造器不会显示出来但它确实存在。这个构造器的存在感以及强度很弱,我们但凡自己写一个构造器,就不能调用它了,如下:
person类中现在没有人为书写的构造器,因此在声明对象类型变量的时候我们使用无参构造器,一切正常,世界仍然充满了爱。然而这时,我们脑子一抽,自己写一个有参数的,和默认构造器不同的构造器:
这时江河开始放异彩了,我们注意荧光笔部分已经报错了,我们这时仍然使用的是默认构造方法在进行声明,这时IDE的自动报错功能给了一个提示,说我们不能这样写,这就是因为默认的构造器强度很弱,导致直接被覆盖了。然而,我们自己写的构造器不会互相覆盖,只要方法签名不一样,它们就可以同时存在并且在进行对象类型变量声明的时候,我们想用哪个用哪个。
如图所示我们重新定义了一个无参构造器,这个无参构造器显然不是默认的而是我们自己写的。这是上边没有错误提示。然而我们不能写方法签名一样的,如图:
我们写了两个方法签名一样但操作不同的构造器,如图中所示,也是直接报错了。
因此,我们发现,在书写一个类时,可以人为的书写构造器,也可以不写,当你不写的时候,类中会默认存在一个啥也不干的默认构造器,它只起到一个功能上占地方的作用,因为Java中对象的声明过程涉及使用构造器,在声明并初始化对象的时候你必须有这个构造器,否则编译器不允许。我们可以将这个现象解释为:在设计Java语言的时候,对象的过程被设计成可以通过构造器进行初始化,为了不添加更多的编译机制,本着如无必要,勿增实体的观点,人们将它设计成了:不管有没有用,必须用构造器,因为这样一来编译器就可以不在判断你的声明方式属于有构造器类型还是无构造器类型了,可以节省时间,但是你再定义时,就必须多打几个字。你可以不写构造器,不初始化你的对象,但是你必须得有。然而这同时导致了即使我们不想初始化一下子对象,也得自己写一个啥也不干的构造器,因此为了让大家轻松,设计者在底层的类代码中加入了一个默认的写死的无参构造器,这样一来大家就没必要写额外的代码了。
设计者同时认为:当你书写了你自己的构造器,那么就是你一定有了自己的考量,想要对你的对象进行你想要的初始化,这时你肯定就用不上默认的构造器了,因为默认构造器啥也干不了,只是一个占位用的,你自己写的构造器能干它所用可以干的,因此这时它就失去了存在的必要,这时它就会失效,因此一旦我们书写了自己的构造器,默认构造器就会失效。当然我们如果想继续使用无参构造器,还可以自己写,只不过之前的啥也不干的构造器就用不了了。
在面向对象编程中,少不了权限的界定,不同的访问权限对封装特性有着很大的帮助。接下来我们会学习几个修饰权限的关键字:
说到public,我们都会想到publish,它是出版发行的意思,比如我们把自己写的书出版出去,让公共可知。而public的意思就是公共的,大家都可以访问的。一个被publish修饰的东西,它就是谁都可以访问的一个公共字段,这个权限是最开放的。
在一个Java源文件中,被publish修饰的类被称为主类,它必须和源文件同名,且它可以被跨包访问。在其他包中,只要引用过来一个包,就可以直接访问这个被public修饰的类了。
被public修饰的类属性,被称为公共属性,公共属性同样可以被直接访问,知道获取到类的对象,就可以根据这个对象直接访问这个属性;公共方法也是一样,只要拿到这个对象,就可以直接根据对象进行方法调用,这是一个非常常用的修饰符。
需要注意的是,类修饰符只有public,abstract和final,还可以不加修饰符,只有在加了public之后,才可以跨包访问,其余的想要进行访问,都必须在同一个包内才行。
private的意思是私有,这个修饰符可以用来修饰类的属性以及方法,被private修饰的属性或者方法,只能在这个类内部被使用。对于私有属性,我们一般是为了保护信息的安全性而设置,我们可以通过访问器和修改器对其进行安全的修改和访问。私有方法一般是仅存在于类内部的方法,外部不能调用。
final修饰的变量被称为叫常量,它只能被赋值一次,它在被第一次赋值之后,就不能被改变了。因此我们称之为常量,按照命名规范,它的变量名必须全是英文大写。常量被声明之后可以不被赋值,并且可以被使用,因为在Java中每个变量都有一个默认值0,但通常来说使用这个值是没有什么意义的。常量被赋值之后,就不能被在此赋值,否则会报错。
需注意的是,被final修饰的引用类型变量的值是可以改变的,因为引用类型的句柄的值实际上是一个地址,因此变得不可改变的实际上是这个地址,这个地址上边的值是可以被改变的,如一个final类型的数组变量,我们仍然可以修改数组中的元素值,但是我们不能为这个变量进行拓展数组了,因为它被final限制死了,只能指向一个地址,不能再指向其他地址。
final修饰全局变量必须赋值,修饰局部变量可以不赋值。因为final修饰的变量只能赋值一次,int值默认0,定义后不赋值系统会认为它无意义,系统不允许这种无意义的事情发生,直接使用默认值的方法不标准且不严谨。被final修饰的变量不叫变量了,因为它固定了一块地址的值,不允许改变了,因此它叫常量。
关于final,在之前的笔记上也有一个比较详细的知识点讲解,这里是一个面试点,需要注意。
何为类,何为对象?类与对象的关系 Public class Demo{ int age;//全局变量 public void dd(){ int a;//局部变量 } } 类是构造对象的模板 构造器:什么是构造器, Java有八种基本数据类型和三种引用数据类型 为什么java要定义数据类型, int a = 1;32bit 1bit 31bit float b = 1.0f;32bit 1bit 8bit 23bit 在计算机底层,不同的数据类型有着不同的存储结构,不同的类型会告诉系统如何开辟存储空间,开辟什么样的存储空间。它会告诉你在内存中如何开辟空间。 Cat cat1 = new Cat() 对象名 new是关键字, new关键字是告诉内存在堆区开辟内存,要创建对象 Cat()是什么,是构造器!new后边这个就是构造器 方法签名指的是方法名和参数列表,也就是可以有同名方法,但不能有同样的方法 构造器是和类名相同的方法 构造器的特点: 1.构造器的名称和类的名称相同 2.构造器没有返回值,没有返回值类型这个关键字部分 3.构造器的最主要特点是初始化对象,并且在创建对象的时候执行其内部的程序。 4.在一个类中有一个或者一个以上的不同的构造器 5.在一个类当中有一个默认的,不显示的,无参构造器,这是最基本的构造器。且这个构造器很容易失效,当我们在这个类当中重写一个构造器的时候,这个默认的构造器将会被覆盖。被覆盖后就彻底不能用了 构造器的最主要的作用就是在创建是给对象赋值,这个可以理解为初始化方法 public是一个权限修饰符,代表谁都可以访问。private,除了本身以外谁都不可访问 public就是其他类可以随便访问,private就是只能在类的内部进行访问 封装特性研究学习一下 final final修饰的变量必须是大写,这个叫常量 final修饰的变量是不可变得,数组这里有个小例外,不可变的是句柄的值,而不是值本身,对于引用类型,变量的值不是值本身,因此这里有一个例外。final修饰的是一块固定地址上的值,这块地址上的值不可被改变,和地址相关的值可以被改变。 final修饰全局变量必须赋值,修饰局部变量可以不赋值。因为final修饰的变量只能赋值一次,int值默认0,定义后不赋值系统会认为它无意义,系统不允许这种无意义的事情发生,直接使用默认值的方法不标准且不严谨。 被final修饰的变量不叫变量了,因为它固定了一块地址的值,不允许改变了,因此它叫常量。