代码关系就是万物关系吗?

11月30日,中国“二十四节气”正式列入联合国教科文组织人类非物质文化遗产名录。已有几千年历史的二十四节气,包含了人们对四季变化转换规律的总结,是祖先留下的宝贵遗产。

恰逢二十四节气申非遗成功之时,著名学者余世存最新力作《时间之书:余世存说二十四节气》顺势推出,画家老树倾力绘制24幅节气美图,成为第一部全面解读二十四节气的国民读本。

近日,新湖南客户端记者对话余世存,听他解读中国人千百年来证实的存在与时间。

每一节气都有它的功用和价值

新湖南:为什么想写下《时间之书》?为什么觉得“时间是最好的朋友”?在这之前,您写过《大时间:重新发现易经》,上次您也说到在研究易经。为何对时间特别感兴趣?

余世存:这本书是应《国家人文历史》杂志之约开专栏的结果。我也曾问一个知名的历史学家,二十四节气是什么时候形成的,历史学家张口结舌。我才意识到,传统文化的这些学问在现代学术中是极为边缘的。就这么写下来,写了整整一年,真正成了我的研究成果。

时间是最好的朋友。对没有阅历和反思生活的人来说,这话没有意义;而对理解它的人来说,这话有各自的场景、感念和安慰。我在写节气时,也体会到人们说的“日日是好日”,每一节气都有它的功用和价值,每一时间都在整体的意义上帮助我们,无论是让我们积累,还是让我们休止。

我们身边大多数人失去了时间感,人人以为自己的生存是一个偶然,存在是唯物的,是当下要牢牢抓住什么的。很少有人意识到自己与时间、空间有最深刻的联系。我们每个人都携带有时空的代码,就是我们彼此交流或档案上的时空信息,我们的这一信息或时空代码与新的时空形成良性、惯性或恶性的互动,但我们对此互动日用而不知。

新湖南:老庄、易经与节气时间,存在某种共通。与现代科学不同,中国文化似乎更倾向于“生命哲学”,一种世界观和宇宙观,我个人认为中医也是如此。您怎么理解时间在中国传统文化和古典中国人中扮演的角色?

余世存:有朋友说,中国文化是时间性的,西方文化是空间性的。时间确实在传统文化和古典中国人那里有重要的意义,比如老庄道家讲长生久视,我们普通人追求好死不如赖活,等等,都是如此。在这个意义上,我们中国人对时间的把握较为精细。无论是大时间尺度的玛雅文化,还是对时间不太关注的印度文化,以及西方文化,都不如中国文化对时间的感受敏锐细腻。

但有意思的是,中国人的时间意识是循环的,不是线性演进的,中国人对时间虽然重视,在西方人眼里,却是长期停滞的文明。黑格尔就感叹中国是一个没有时间的国度。

写时间简史,看到先人对节气时间的理解,就像是一个非常有审美能力、有科学精神也有善意的人,在与时间进行莫逆于心的互动,是一个人与天地精神相往来。这个人就是我们中国的古典文化。

真正的信仰,其发生源起就是先民对时空大化的感受

新湖南:《时间之书》的远景似乎是老庄哲学,但具体篇目和表达又是以儒家仁、义、礼、智、信、恕、忠、孝、悌等德目来遵循、恪守时间和环境之变?儒和道如何在您身上融合?

余世存:你的这个感受是对的。我说过,宋明以后,儒释道在中国上层、下层都实践着合流的历史过程。今天的中国人尤其是知识人,如果还强行把它们分开来取法就有问题。我在《立人三部曲》的序里还说过,儒释道耶回都是现代人的思想资源,如果一定要区分出中国人与他者,那么中国人就是内释(儒释道)外耶(耶回)。

我这本书的序题叫“行夏之时”,孔子说他的为邦之道是,“行夏之时,乘殷之辂,服周之冕”,我发挥孔子的这话,在全球化时代,孔子的“行夏之时”之说,就是采用公历时间,享用各国产品,保留中国元素,怀抱人类情怀。

新湖南:您曾写过《非常道》《中国男》《小民大国》等,被誉为青年学者、思想家、公共知识分子。后来开始转向研究老子、易经、时间,您如何看待自己身上的这种“转变”?现在如何看待“社会”与“自身”的关系,如何理解信仰?

余世存:这种转变是从青春走向中年的必然,跟从热烈的生活退回宁静的书斋没有关系。我的文字不是学院的、书斋的,我的文字仍是战斗的、撄人心的。在某种意义上,我对做一个知识人有些自觉,我们身上背负了五四新文化运动以来的知识或思想使命,我们的努力仍在给社会提供有效的思想资源,仍在校正、安顿世道人心。

我这些年经常使用“绑架”一个词,我们自身的生活或使命不能为时代社会裹胁绑架,要活出自己并不容易,这需要有至高的信仰。真正的信仰,在其发生源起上,就是先民对时空大化的感受。它跟我们现在的拜物教、拜金主义、物质主义一类的信仰不是一回事。

节气时间中的物候是比GDP更重要的生存指标

新湖南:古老的农耕文明日渐消隐,牧歌已经远去,作为中国古人生产生活的指南,这种根植于时间中的生活方式和生存观,您认为还在对我们的现代生活施加怎样的影响?我们为何还需要它?

余世存:我的理解是,现代生活有超越时空的一面,但同时仍离不开具体时间,对时间的感受和安排,仅仅以一分为四的一年四季来做参照是不够的;以一分为十二的月份时间来做参照也是不够的。只有把24份的节气时间纳入进来,现代生活的时间才不仅仅只是人文的,人道的,而是涵有自然、天道的。

随着节气时间从传统中从背景里走出来,参与我们当下的生活,我们会创造出更丰富多样的生活。我们会用节气时间来校正网络时间、手机刷屏时间、技术跟风时间、拜物拜金时髦时间、单位变异时间,等等,我们会从日常生活的理性或物质生活理性的束缚中解放出来,那种人性的、生命无限丰富可能性的东西会在我们身上复苏。

所以,我觉得从节气时间开始,我们个体的日常生活和人生感受的时间才算真正开始了。年轻朋友借助于节气时间,可以认知自己的生命已经完成的阶段和有可能抵达的境界。

新湖南:节气、物候在当下似乎更趋向于一种纯粹美学上的理解和感受,这一关于时间的文化和文明模式,如何体现它的“旧邦新命”?

余世存:节气时间中的物候是一个非常好的生存指标,比如过去的乡村,春分前后,燕子是否飞回自己家里安居是一个有意义的事件。如果自己堂屋里没有燕子安个窝,对人的打击是很大的。我相信,今天的节气时间会出现新的物候,这些物候会让我们对生存环境,对自身和共同体的德性有一个及时的检验和考量。

比如霾已经是一个重要的物候了,可以说,节气时间中的物候是比GDP指标、物价、房价更重要的参数。

现代人最大的问题之一是丧失时间感,身心紊乱焦虑

新湖南:我们有自己的生物钟,可以说,每个人的身体里都存在一个时间。如何调和身体内外的时间,是一个隽永又优美的奥秘。能否谈谈您理解的人在时间中的使命。

余世存:比如立春节气是“天下雷行而育万物”,这是提醒人们春天是生发的季节,植物、动物都生发繁荣的时候,这个时候是不能伤春的。所以接下来的雨水节气是“君子以思患预防”,既说明君子应该注意不干预自然界的生发,也应该注意周围是否因为雨水不够而难以生发。

说到这里,还有一个故事我没写进书里。咸丰皇帝的才智一般,比不上兄弟恭亲王,但他让父亲看重立为太子就是因为他的老师教了他几招,其中一招就是,跟众王子一起打猎时他一箭未放,父亲问他,他回答说:“此方春,鸟兽孳育,不忍伤生以干天和。”据说他的父亲道光皇帝称道他:“此真帝者之言!”从这个故事说明,人在春天开始也要生发,但要注意有所约束,不能因为自己要发展而毁灭了他者。

新湖南:我们如何调整、确定、开始自己的时间,如何在时间中获得安顿?

余世存:现代人最大的问题之一就是丧失时间感。全球化的人类文明的成果之一就是现代人对时间空间的超越,而忽略了人是此时此地的存在,人的身心仍有生物钟。如果时间感丧失,生物钟紊乱,人的身体和精神状态就会出问题。

当然,时间感对现代人的意义不仅仅是健康方面的,也有人生幸福和意义方面的。从时间感的角度,我们可以观察很多社会现象。比如“成功人士”这个词,只中性地说它的人生阶段的话,它基本上是指人生秋冬之际的状态;但年轻人正处在人生的春夏阶段,所以年轻人根本不应该羡慕成功人士的生活,也不应该仿效成功人士的生活。因为春夏阶段,还是要耕耘,要播种,要吃苦。我在书的自序里引过一段话:“年轻人,你的职责是平整土地,而非焦虑时光。你做三四月的事,在八九月自有答案。”但现实社会里,很多年轻人却没怎么付出就想过成功人士的生活,不仅痴心妄想,而让自己的精神焦虑不安,酿成自己人生和社会的种种悲喜剧。

那么成功人士呢?秋冬阶段的人生应该怎么度过呢?从节气时间来说,秋冬阶段,既是收获的季节,又是散财的季节。很多人把春夏秋冬的生长收藏当作人生指南,但最后一个藏字,一般人的理解是有问题的。藏不是聚敛的意思,不是囤积的意思,对人生人身来说,它更是清廉的意思,是退场的意思,更是洗心退藏于密而生出智慧的意思。成功人士的生活,有一个重要的考量,就是他是否退场,是否散财;如果他只是把财富收割了藏在自己身上,那就会招来灾害。老子说过,多藏必厚亡。那种贪婪收藏而非清廉散财布道布施的生活,会把这种成功人士钉在人生社会的耻辱柱上。

新湖南:日常生活中,您如何与身体内外的时间相处?您现在如何理解时间?

余世存:我也是在向古典中国人学习,比如苏东坡、朱熹、王阳明、顾炎武这些人的实修功夫很不错,我也曾经从静坐、站桩、持戒中受益,感受身体内外的差异。回到身体内的“知止知定”中,是对身外热闹繁杂世界的一种有效的补充和校正。

上药三品,神与气精;把精气神安顿调伏把握好了,可以说是给身心充电补充能量。当然,我现在的功夫还比较浅,还没有修到出入自如的地步。

我同意康德意义上的理解,时间是我们的先天直觉形式。我们个人获得时间感,意味着我们的“重生”或“再生”;但很多人没有自己的时间感,他们只有社会的时间感、圈子的时间感、单位的时间感,他们的人生就只是本能的、因循的、一次性的。

狗、房子这种具体的事物是对象,“服务”这种抽象的概念也是对象。

你可以用对象来存储东西。狗对象可以存储狗头,狗腿等。“服务”对象可以存储服务类型、服务员、顾客等。

你可以要求对象执行某种操作。比如,让狗叫一声,让“服务”对象做一个“送货上门”的动作。

程序中的各个对象通过发送消息来告诉彼此要做什么,来合作完成一项任务。比如你调用某个对象的某个方法,“调用”的过程就是“发消息”。

3,一个对象可以由许多其他对象组成。

4,每一个对象都有自己的类型。

比如,A a=new A();那么,a的类型就是A,“类”就是“类型”。你创建了一个狗对象,那么,狗对象的类型就是狗。

类与类区分开来的一个重要因素是:“可以发送什么样的消息给它”,即:每个类都有自己的方法。

5,某一特定类型的对象可以接收同样的消息。

接收消息即调用方法。泰迪类、博美类都属于狗类。上面这句话的意思就是:因为泰迪和博美都是狗,所以,它们都能够调用狗的“叫”、“跑”等方法。比如:

BoMei和TaiDi继承了Dog,所以,它们创建的对象可以调用Dog的方法。

这就意味着,我们要编写BoMei或者TaiDi相关的代码时,只需要编写Dog相关代码就可以,这就是OOP(面向对象设计)中最强有力的概念之一。如下:

我给Dog定义了legs属性。在test(Dog dog)方法中,我传入的参数是“Dog”,操作的也是Dog类的属性。但是当我在16行调用这个方法时,我传的参数是“BoMei”对象,此时,操作的就是BoMei。

6,每个对象都是唯一的

对象都有状态、行为、标识、类型。比如上面的BoMei类,“状态”就是“变量”,就是“属性”,上面的BoMei类,legs就是它的“状态”。行为就是“方法”、“函数”,对应上面的bark()、eat()等。行为可以改变状态,比如上面的test(Dog dog)改变了legs属性的值。类型就是对象所属的类,而“标识”就相当于身份证一样,这在java中表现为:每个对象在内存中都有唯一的地址。

二、每个对象都有一个接口

苹果有皮、有核,是甜的,长在树上。梨也有皮,也有核,也是甜的,也长在树上。那么苹果和梨有很多相似性,被划分到同一类型:水果类。这个“水果类”就是一个抽象数据类型(所谓的抽象,就是抽取了一些类似的表象东西,比如皮、核、味道、生长环境等)。

抽象数据类型的运作方式与基本数据类型是一致的。比如对于基本数据类型,可以这样使用:

对于抽象数据类型,可以这样使用:

创建某一类型的变量(创建对象或者实例),并调用其方法(发送消息或请求,让它知道该做什么)。

每个具体的对象又有自己特有的状态,比如,苹果的皮是红的,梨子的皮是绿的。苹果水分少,梨子水分多等。所以,苹果、梨都是水果类,但是它们又是水果类中独立的个体,这些个体就是一个个唯一的对象,那么对象与类的关系就明了了:每一个对象都属于定义了特性和行为的某个特定的类。编程系统对待抽象数据类型与对待一般数据类型是一视同仁的,也会作类型检查等:

苹果和梨子都能吃,能榨果汁,能制作苹果罐头等。吃、榨果汁、制作罐头这属于行为(也就是方法,也就是请求),苹果和梨子都满足这些特定的请求,那么这些特定的请求就定义在接口中,而接口也是一种类型:

接口只是定义了可以向某一特定对象发出的请求,那么具体到苹果、梨子该发出怎样的请求,剥皮吃还是不剥皮,榨果汁时要注意什么,这都是具体请求该有的细节,在苹果类、梨子类中必须有这些具体的细节方法,这些代码就构成了“实现”。

三、每个对象都提供服务

不要试图把所有功能都拥挤在一个对象里,完成一项服务项目需要各个对象的配合,比如实现一个打印模块,我们可以定义一个对象专门检查配置是否正常,另一个对象定义怎样打印一张4A图纸,再定义一个对象集合,调用前两个对象,再加之自己的方法最终把图纸打印出来。每个对象都可以很好的完成一个任务,但并不试图做更多的事情。然后这些对象齐心协力去完成一项服务。

上述的“齐心协力”其实就是软件设计的基本质量要求之一:高内聚

Java用三个关键字控制了变量及方法的访问:public、private、protected。public其他类都可以访问,private只有本类及类的内部方法能够访问,其他类不可见。protected表示只有本类及继承本类的子类可见,其他不可见。除此之外,Java还有一种默认的访问权限:包访问权限,类可以访问同一个包中的其他类成员。

访问控制的原因1:让客户端程序员(类的调用者)无法触及他们不该触及的部分。调用者只需要调用有用的方法,有些变量等是设计者为了实现内部逻辑而使用的,不需要让客户端程序员知道。如果把那些私有变量公开化,既会干扰到客户端程序员的调用思路,也可能被客户端程序员误操作而修改了状态值。

访问控制的原因2:类库的设计者可以改变内部的工作方式而不会影响到外部客户端程序员的调用。

五、组合,聚合,代码复用

代码复用是面向对象程序设计语言所提供的最了不起的优点之一。

直接用某个类创建一个对象也属于复用,下面第6行就是在复用Apple类。

将某个类的对象置于某个新类中(创建一个成员对象),也属于复用:

上例中是用Fruit、Dog合成了Test类,所以,这个概念称为:组合(composition)。如果组合是动态发生的,则称为“聚合(aggregation)”。

什么是动态发生呢?看:

第7行并没有创建Apple实例,等到11行调用时,才实例化了Apple,这就是动态发生。

组合的关系是:has-a,即:汽车拥有引擎、公司拥有员工、Test拥有Fruit、Dog。

复制现有的类,然后添加和修改这个复制品来创建新类,这就是继承。当源类(又叫:父类、超类、基类)发生变动时,被修改的子类也会发生这些变动。

上面Circle继承了Shape,自然也就继承了Shape的color属性,以及getColor()、setColor()方法、draw()方法。在继承过来的同时,Circle修改了draw()方法,表现出自己与父类不同的地方(这一行为叫做override,即:覆盖)。当然,Circle也可以添加自己的新方法。

如果我把父类的getColor方法修改一下:

那么子类的这个方法就跟着发生了改变。

父类含有所有子类所共享的特性和行为。比如上例,color变量是共享的(color属于特性),draw()方法也是共享的(draw()属于行为)。

父类与子类有着相同的类型。

从上面代码可以看出,Circle类型同时也是Shape类型,这个很容易理解,博美是狗,泰迪是狗,圆是图形,没毛病。

理解面向对象设计的重要门槛是理解:通过继承而产生的类型等价性

使父类与子类产生差异的两种方法:

1, 添加新方法。(此时,子类与父类的关系是:is like a)

2, 覆盖。(只覆盖而不添加新方法的话,子类与父类的关系是:is a)。

上面代码中,test()里面的参数为父类Shape,调用的也是父类Shape的draw()方法。当在main方法中调用test时,却传入了子类Circle对象,test方法也可以正常调用,且这时,调用的是Circle的draw()方法。

经常需要这样,把一个子类对象当做它的父类型来对待,这样做的好处是,不依赖特定类型的代码,也不受添加新类型的影响。比如,下一次你传入一个正方形子类,那么test内部就会自动调用正方形的draw()方法。你如果添加一个新类:六角形,然后把六角形对象传入test,它也能够正常调用。

面向对象设计语言采用的是“后期绑定”。当test()方法具体调用时,才能确定参数所对应的具体类型。

上述把子类当做父类型的过程叫做“向上转型,upcasting”。

我们有时需要管理很多对象,我们不知道需要多少个对象,不知道这些对象能够活多久,不知道存储这些对象需要多大空间,一问三不知。

容器(集合)帮助我们解决了上述问题。我们把对象放入容器中,在任何时候都可以去扩充它。

Java中具有满足各种需要的容器。比如:List(有序的对象集合),Map(建立对象之间的关联,也叫映射),Set(每种对象只有一个,不会重复,类似于数学中的集合),当然,还有队列(先放进去的对象先出来,FIFO)、树(以任意顺序把许多对象放进该容器,当你遍历时每个值已经排好序)、堆栈(最后放进去的对象先出来,LIFO队列)等。

把”张三”与”NAME”关联,把”17”与”AGE”关联,这样,在获取”张三”时,只需要如下:

1,不同的容器作用不同(接口不同、方法不同)。

比如,Stack和Queue二者不在一个继承树下,且各自的方法功能不相同。实际上,Stack(堆栈)是一种后进先出的模式,只能在栈头进行插入与删除操作。Queue(队列)是一种先进先出的模式,只能在队尾进行插入,在队头进行删除。

2,不同的容器性能不同

比如:ArrayList和LinkedList。如果是随机访问一个元素,对于ArrayList来说时间是固定的,而LinkedList需要从第一个元素开始查找,直到找到目标元素,如果目标元素在容器的末端,那么就要花费更多时间。

如果是插入一个元素,对于ArrayList,插入位置后面的元素统统要往后移动一位(想想实际生活中的插队),而对于LinkedList来说,只需要把插入位置的两个对象拆开,然后把目标元素加入进来即可(想想实际生活中小朋友们手拉手的情况)。下图是LinkedList的插入与删除:

Java中,所有对象都继承自Object,那么由向上转型规律可知,能存放Object的容器,就能存放任何Java对象。

其实容器里放置的并不是对象本身,而只是对象的”引用”,指向对象的地址。

当你把一个非Object对象(比如String)的引用放进一个容器时,由于该容器只能存放Object引用,所以,它将会强制转成Object引用。那么当你再次取出该引用时,就变成了Object引用,而不是你想要的String。如下:

虽然存入List中的是dog的引用,而把它赋值给dog2时,却报错,因为dog.get(0)取出来的是Object类型的引用。这时,就要用到向下转型。

向上转型是把子类当作父类来用,而向下转型是把父类型转换为一个更具体的类型:

比如上图,把Object引用强制转换为Dog。

向上转型是安全的,比如你可以大胆的说:苹果是水果,梨子是水果,所以,你可以大胆的把任何对象强制转成Object:

向下转型却是不安全的,你只知道它是一条狗,但是你不知它具体是什么种类:

上例把泰迪放入List,取出来时是Object,却错误的把它转换成博美,结果发生了错误。

为了避免上面种种风险的发生,Java中引入了泛型:

如上图,明确说明List狗窝中只能放泰迪,那么你放入博美就会直接报错。

如上图,我已经知道List狗窝里面睡的是泰迪,你取出来把它喊成博美,那么也会报错。

当你每次new一个对象时,Java就动态地在一个称为“堆”的内存池中创建一个对象。由于是动态的,所以,直到运行时才能知道要创建多少个对象,对象的生命周期是什么,以及对象的具体类型是什么。再搬出前面出现过的一段代码来加深对“动态”的了解:

上例中,直到运行test()时才会创建Apple实例。

Java的垃圾回收机制会自动发现对象什么时候不用了,然后去销毁它,以达到释放内存的目的。

Java判断某个对象是否可以回收是一件复杂的事情。比如,一般情况下,创建一个对象,会在堆中生成一个对象,而会在栈中放一个该对象的引用(一个数字,指向这个对象在堆中的地址),垃圾回收器有一种早期策略,每生成一个引用,计数器就会+1,而每减少一个引用,计数器就会-1,当发现计数器为0时,说明该对象可以回收了。如下图:

图中第四步,per2指向了per1引用的地址,那么就没有引用指向age=20的那个对象了,于是,那个对象会被垃圾回收器回收。

异常是一种对象,当程序发生错误时,它从那个错误点“抛出”,然后由该错误对应的专门的处理器“抓住”。所以,如果代码是正常的,异常代码将不会发生。

有些异常是在运行的时候才能发现的,主要是由于程序员的失误造成的,这类异常叫做“运行时异常”,比如:

你明知道test不能转成整数,还非要转,运行时就会报错。

还有一种异常,叫“非运行时异常”,就是运行前就该有所防范的,比如想从某个路径下加载一个文件,这种情况下,就有找不到这个文件的可能性,那么,你必须做出防患于未然,可以抛出异常:

为了提高响应能力,我们想把一个任务分成多个子任务独立的运行,让他们一起干活。这些独立运行的子任务就是”线程”,而“一起干活”就是“并发”。

实际上,在单一处理器环境中,线程之间是轮番交叉运行的,由处理器来分给每个线程时间。而在多处理器上才存在真正的”一起干活”,即“并行”。

多线程并发完成一项工作的过程中,资源共享就带来隐患,比如桌子上有一个粉笔(资源),两个小朋友(线程)同时伸手去拿粉笔(抢占资源),那么就会打起来。所以,就要有一种机制,当一个小朋友要去拿粉笔时,先把粉笔保护起来(加锁),等这个小朋友不用了,再释放锁,另一个小朋友才可以用粉笔。

转自公众号:码农求职小助手

1、解释下什么是面向对象?面向对象和面向过程的区别?

面向对象是一种基于面向过程的编程思想,是向现实世界模型的自然延伸,这是一种“万物皆对象”的编程思想。由执行者变为指挥者,在现实生活中的任何物体都可以归为一类事物,而每一个个体都是一类事物的实例。面向对象的编程是以对象为中心,以消息为驱动。

区别:(1)编程思路不同:面向过程以实现功能的函数开发为主,而面向对象要首先抽象出类、属性及其方法,然后通过实例化类、执行方法来完成功能。 (2)封装性:都具有封装性,但是面向过程是封装的是功能,而面向对象封装的是数据和功能。 (3)面向对象具有继承性和多态性,而面向过程没有继承性和多态性,所以面向对象优势很明显。2、面向对象的三大特性?分别解释下?
(1)封装:通常认为封装是把数据和操作数据的方法封装起来,对数据的访问只能通过已定义的接口。 (2)继承:继承是从已有类得到继承信息创建新类的过程。提供继承信息的类被称为父类(超类/基类),得到继承信息的被称为子类(派生类)。 (3)多态:分为编译时多态(方法重载)和运行时多态(方法重写)。要实现多态需要做两件事:一是子类继承父类并重写父类中的方法,二是用父类型引用子类型对象,这样同样的引用调用同样的方法就会根据子类对象的不同而表现出不同的行为。

(1)子类拥有父类对象所有的属性和方法(包括私有属性和私有方法),但是父类中的私有属性和方法子类是无法访问,只是拥有。因为在一个子类被创建的时候,首先会在内存中创建一个父类对象,然后在父类对象外部放上子类独有的属性,两者合起来形成一个子类的对象; (2)子类可以拥有自己属性和方法; (3)子类可以用自己的方式实现父类的方法。(重写)3、JDK、JRE、JVM Machine):是 Java 虚拟机,是整个 Java 实现跨平台的最核心的部分,能够运行以 Java 语言写作的软件程序。所有的 Java 程序会首先被编译为 .class 的类文件,这种类文件可以在虚拟机上执行。4、重载和重写的区别?
(1)重载:编译时多态、同一个类中同名的方法具有不同的参数列表、不能根据返回类型进行区分【因为:函数调用时不能指定类型信息,编译器不知道你要调哪个函数】; (2)重写(又名覆盖):运行时多态、子类与父类之间、子类重写父类的方法具有相同的返回类型、更好的访问权限。
Java 中 static 方法不能被覆盖,因为方法覆盖是基于运行时动态绑定的,而 static 方法是编译时静态绑定的。static 方法跟类的任何实例都不相关,所以概念上不适用。 Java 中也不可以覆盖 private 的方法,因为 private 修饰的变量和方法只能在当前类中使用, 如果是其他的类继承当前类是不能访问到 private 变量或方法的,当然也不能覆盖。

静态的方法可以被继承,但是不能重写。如果父类和子类中存在同样名称和参数的静态方法,那么该子类的方法会把原来继承过来的父类的方法隐藏,而不是重写。通俗的讲就是父类的方法和子类的方法是两个没有关系的方法,具体调用哪一个方法是看是哪个对象的引用;这种父子类方法也不在存在多态的性质。

6、构造器是否可以被重写?

在讲继承的时候我们就知道父类 的私有属性和构造方法并不能被继承,所以 Constructor 也就不能被 O verride(重写),但是可以 Overload(重载),所以你可以看到一个类中有多个构造函数的情况。7、构造方法有哪些特性?(1)名字与类名相同; (2)没有返回值,但不能用 void 声明构造函数;
(3)成类的对象时自动执行,无需调用。

8、在 Java 中定义一个不做事且没有参数的构造方法有什么作用?

Java 程序在执行子类的构造方法之前,如果没有用 super() 来调用父类特定的构造方法,则会调用父类中“没有参数的构造方法” 。因此,如果父类中只定义了有参数的构造方法,而在子类的构造方法中又没有用 super() 来调用父类中特定的构造方法,则编译时将发生错误,因为 Java 程序在父类中找不到没有参数的构造方法可供执行。解决办法是:在父类里加上一个不做事且没有参数的构造方法。9、Java 中创建对象的几种方式?1、使用 new 关键字; 2、使用 Class 类的 newInstance 方法,该方法调用无参的构造器创建对象(反射):Class.forName.newInstance(); 3、使用 clone() 方法; (1)抽象类中可以定义构造函数,接口不能定义构造函数; (2)抽象类中可以有抽象方法和具体方法,而接口中只能有抽象方法(public abstract); (3)抽象类中的成员权限可以是 public、默认、protected(抽象类中抽象方法就是为了重写,所以不能被 private 修饰),而接口中的成员只可以是 public(方法默认:public abstrat、成员变量默认:public static final); (4)抽象类中可以包含静态方法,而接口中不可以包含静态方法;

1、在 JDK1.8中,允许在接口中包含带有具体实现的方法,使用 default 修饰,这类方法就是默认方法。

2、抽象类中可以包含静态方法,在 JDK1.8 之前接口中不能包含静态方法,JDK1.8 以后可以包含。之前不能包含是因为,接口不可以实现方法,只可以定义方法,所以不能使用静态方法(因为静态方法必须实现)。现在可以包含了,只能直接用接口调用静态方法。JDK1.8 仍然不可以包含静态代码块。

11、静态变量和实例变量的区别?
静态变量:是被 static 修饰的变量,也称为类变量,它属于类,因此不管创建多少个对象,静态变量在内存中有且仅有一个拷贝;静态变量可以实现让多个对象共享内存。 实例变量:属于某一实例,需要先创建对象,然后通过对象才能访问到它。
和 int 的区别?(1)int 是 Java 的八种基本数据类型之一,而 Integer 是 Java 为 int 类型提供的封装类; (2)int 型变量的默认值是 0,Integer 变量的默认值是 null,这一点说明 Integer 可以区分出未赋值和值为 0 的区分; (3)Integer 变量必须实例化后才可以使用,而 int 不需要。

1、由于 Integer 变量实际上是对一个 Integer 对象的引用,所以两个通过 new 生成的 Integer 变量永远是不相等的,因为其内存地址是不同的;

2、Integer 变量和 int 变量比较时,只要两个变量的值是相等的,则结果为 true。因为包装类 Integer 和基本数据类型 int 类型进行比较时,Java 会自动拆包装类为 int,然后进行比较,实际上就是两个 int 型变量在进行比较;

自动装箱是 Java 编译器在基本数据类型和对应得包装类之间做的一个转化。比如:把 int 转化成 Integer,double 转化成 Double 等等。反之就是自动拆箱。

在 switch(expr 1) 中,expr1 只能是一个整数表达式或者枚举常量。而整数表达式可以是 int 基本数据类型或者 Integer 包装类型。由于,byte、short、char 都可以隐式转换为 int,所以,这些类型以及这些类型的包装类型也都是可以的。而 long 和 String 类型都不符合 switch 的语法规定,并且不能被隐式的转换为 int 类型,所以,它们不能作用于 switch 语句中。不过, 需要注意的是在 JDK1.7 版本之后 switch 就可以作用在 String 上了。16、字节和字符的区别?字节是存储容量的基本单位; 字符是数字、字母、汉字以及其他语言的各种符号; 1 字节 = 8 个二进制单位,一个字符由一个字节或多个字节的二进制单位组成。17、String 为什么要设计为不可变类?

在 Java 中将 String 设计成不可变的是综合考虑到各种因素的结果。主要的原因主要有以下三点:

(1)字符串常量池的需要:字符串常量池是 Java 堆内存中一个特殊的存储区域, 当创建一个 String 对象时,假如此字符串值已经存在于常量池中,则不会创建一个新的对象,而是引用已经存在的对象;(2)允许 String 对象缓存 HashCode:Java 中 String 对象的哈希码被频繁地使用, 比如在 HashMap 等容器中。字符串不变性保证了 hash 码的唯一性,因此可以放心地进行缓存。这也是一种性能优化手段,意味着不必每次都去计算新的哈希码; (3)String 被许多的 Java 类(库)用来当做参数,例如:网络连接地址 URL、文件路径 path、还有反射机制所需要的 String 参数等, 假若 String 不是固定不变的,将会引起各种安全隐患。18、String、StringBuilder、StringBuffer 的区别?String:用于字符串操作,属于不可变类;【补充:String 不是基本数据类型,是引用类型,底层用 char 数组实现的】 StringBuilder:与 StringBuffer 类似,都是字符串缓冲区,但线程不安全; StringBuffer:也用于字符串操作,不同之处是 StringBuffer 属于可变类,对方法加了同步锁,线程安全。

在执行 String str1 = "abc" 的时候,JVM 会首先检查字符串常量池中是否已经存在该字符串对象,如果已经存在,那么就不会再创建了,直接返回该字符串在字符串常量池中的内存地址;如果该字符串还不存在字符串常量池中,那么就会在字符串常量池中创建该字符串对象,然后再返回。所以在执行 String str2 = "abc" 的时候,因为字符串常量池中已经存在“abc”字符串对象了,就不会在字符串常量池中再次创建了,所以栈内存中 str1 和 str2 的内存地址都是指向 "abc" 在字符串常量池中的位置,所以 str1 = str2 的运行结果为 true。

而在执行 String str3 = new String("abc") 的时候,JVM 会首先检查字符串常量池中是否已经存在“abc”字符串,如果已经存在,则不会在字符串常量池中再创建了;如果不存在,则就会在字符串常量池中创建 "abc" 字符串对象,然后再到堆内存中再创建一份字符串对象,把字符串常量池中的 "abc" 字符串内容拷贝到内存中的字符串对象中,然后返回堆内存中该字符串的内存地址,即栈内存中存储的地址是堆内存中对象的内存地址。String str4 = new String("abc") 是在堆内存中又创建了一个对象,所以 str 3 == str4 运行的结果是 false。str1、str2、str3、str4 在内存中的存储状况如下图所示:


21、String 类的常用方法都有那些?
indexOf():返回指定字符的索引。 charAt():返回指定索引处的字符。 replace():字符串替换。 trim():去除字符串两端空白。 split():分割字符串,返回一个分割后的字符串数组。 getBytes():返回字符串的 byte 类型数组。 length():返回字符串长度。 toLowerCase():将字符串转成小写字母。

可以。final 修饰的是一个引用变量,那么这个引用始终只能指向这个对象,但是这个对象内部的属性是可以变化的。

23、Object 的常用方法有哪些?
clone 方法:用于创建并返回当前对象的一份拷贝;
getClass 方法:用于返回当前运行时对象的 Class; toString 方法:返回对象的字符串表示形式;
finalize 方法:实例被垃圾回收器回收时触发的方法; equals 方法:用于比较两个对象的内存地址是否相等,一般需要重写; hashCode 方法:用于返回对象的哈希值; notify 方法:唤醒一个在此对象监视器上等待的线程。如果有多个线程在等待只会唤醒一个。
notifyAll 方法:作用跟 notify() 一样,只不过会唤醒在此对象监视器上等待的所有线程,而不是一个线程。
一个很明显的原因是 Java 提供的锁是对象级的而不是线程级的,每个对象都有锁,通过线程获得。如果线程需要等待某些锁,那么调用对象中的 wait() 方法就有意义了。如果 wait() 方法定义在 Thread 类中,线程正在等待的是哪个锁就不明显了。简单的说,由于 wait,notify 和 notifyAll 都是锁级别的操作,所以把他们定义在 Object

final:用于声明属性、方法和类,分别表示属性不可变、方法不可覆盖、被其修饰的类不可继承; finally:异常处理语句结构的一部分,表示总是执行;
finallize:Object类的一个方法,在垃圾回收时会调用被回收对象的finalize。
26、finally 块中的代码什么时候被执行?

在 Java 语言的异常处理中,finally 块的作用就是为了保证无论出现什么情况,finally 块里的代码一定会被执行。由于程序执行 return 就意味着结束对当前函数的调用并跳出这个函数体,因此任何语句要执行都只能在 return 前执行(除非碰到 exit 函数),因此 finally 块里的代码也是在 return 之前执行的。

27、finally 是不是一定会被执行到?

不一定。下面列举两种执行不到的情况:

会。程序在执行到 return 时会首先将返回值存储在一个指定的位置,其次去执行 finally 块,最后再返回。因此,对基本数据类型,在 finally 块中改变 return 的值没有任何影响,直接覆盖掉;而对引用类型是有影响的,返回的是在 finally 对 前面 return 语句返回对象的修改值。

29、try-catch-finally 中那个部分可以省略?catch 可以省略。try 只适合处理运行时异常,try+catch 适合处理运行时异常+普通异常。也就是说,如果你只用 try 去处理普通异常却不加以 catch 处理,编译是通不过的,因为编译器硬性规定,普通异常如果选择捕获,则必须用 catch 显示声明以便进一步处理。而运行时异常在编译时没有如此规定,所以 catch 可以省略,你加上 catch 编译器也觉得无可厚非。30、static 关键字的作用?
(1)静态变量:又称为类变量,也就是说这个变量属于类的,类所有的实例都共享静态变量,可以直接通过类名来访问它。静态变量在内存中只存在一份; (2)静态方法:静态方法在类加载的时候就存在了,它不依赖于任何实例。所以静态方法必须有实现,也就是说它不能是抽象方法。只能访问所属类的静态字段和静态方法,方法中不能有 this 和 super 关键字; (3)静态语句块:静态语句块在类初始化时运行一次; (4)静态内部类:非静态内部类依赖于外部类的实例,而静态内部类不需要。静态内部类不能访问外部类的非静态的变量和方法;

存在继承的情况下,初始化顺序为: 1. 父类(静态变量、静态语句块) 2. 子类(静态变量、静态语句块) 3. 父类(实例变量、普通语句块) 4. 父类(构造函数) 5. 子类(实例变量、普通语句块) 6. 子类(构造函数)31、super 关键字的作用?
(1)访问父类的构造函数:可以使用 super() 函数访问父类的构造函数,从而委托父类完成一些初始化的工作。 (2)访问父类的成员:如果子类重写了父类的某个方法,可以通过使用 super 关键字来引用父类的方法实现。
(3)this 和 super 不能同时出现在一个构造函数里面,因为 this 必然会调用其它的构造函数,其它的构造函数必然也会有 super 语句的存在,所以在同一个构造函数里面有相同的语句,就失去了语句的意义,编译器也不会通过。
对于不想进行序列化的变量,使用 transient 关键字修饰。 transient 关键字的作用是:阻止实例中那些用此关键字修饰的的变量序列化。当对象被反序列化时,被 transient 修饰的变量值不会被持久化和恢复。transient 只能修饰变量,不能修饰类和方法。

==:如果比较的对象是基本数据类型,则比较的是数值是否相等;如果比较的是引用数据类型,则比较的是对象的地址值是否相等。 equals 方法:用来比较两个对象的内容是否相等。注意:equals 方法不能用于比较基本数据类型的变量。如果没有对 equals 方法进行重写,则比较的是引用类型的变量所指向的对象的地址(很多类重新了 equals 方法,比如 String、Integer 等把它变成了值比较,所以一般情况下 equals 比较的是值是否相等)。

两个对象的 hashCode() 相同,equals() 不一定为 true。因为在散列表中,hashCode() 相等即两个键值对的哈希值相等,然而哈希值相等,并不一定能得出键值对相等【散列冲突】。

这个问题应该是有个前提,就是你需要用到 HashMap、HashSet 等 Java 集合,用不到哈希表的话,其实仅仅重写 equals() 方法也可以。而工作中的场景是常常用到 Java 集合,所以 Java 官方建议重写 equals() 就一定要重写 hashCode() 方法。

对于对象集合的判重,如果一个集合含有 10000 个对象实例,仅仅使用 equals() 方法的话,那么对于一个对象判重就需要比较 10000 次,随着集合规模的增大,时间开销是很大的。但是同时使用哈希表的话,就能快速定位到对象的大概存储位置,并且在定位到大概存储位置后,后续比较过程中,如果两个对象的 hashCode 不相同,也不再需要调用 equals()

所以从程序实现原理上来讲的话,既需要 equals() 方法,也需要 hashCode() 方法。那么既然重写了 equals(),那么也要重写 hashCode() 方法,以保证两者之间的配合关系。

1、如果两个对象相等,则 hashCode 一定也是相同的; 2、两个对象相等,对两个对象分别调用 equals 方法都返回 true; 3、两个对象有相同的 hashCode 值,它们也不一定是相等的; 4、因此,equals 方法被覆盖过,则 hashCode 方法也必须被覆盖; 5、hashCode() 的默认行为是对堆上的对象产生独特值。 如果没有重写 hashCode(),则该 class 的两个对象无论如何都不会相等(即使这两个对象指向相同的数据)。36、& 和 && 的区别?

Java 中 && 和 & 都是表示与的逻辑运算符,都表示逻辑运输符 and,当两边的表达式都为 true 的时候,整个运算结果才为 true,否则为 false。

&&:有短路功能,当第一个表达式的值为 false 的时候,则不再计算第二个表达式;
&:不管第一个表达式结果是否为 true,第二个都会执行。除此之外,& 还可以用作位运算符:当 & 两边的表达式不是 Boolean 类型的时候,& 表示按位操作。

37、Java 中的参数传递时传值呢?还是传引用?

Java 的参数是以值传递的形式传入方法中,而不是引用传递。 当传递方法参数类型为基本数据类型(数字以及布尔值)时,一个方法是不可能修改一个基本数据类型的参数。 当传递方法参数类型为引用数据类型时,一个方法将修改一个引用数据类型的参数所指向对象的值。 即使 Java 函数在传递引用数据类型时,也只是拷贝了引用的值罢了,之所以能修改引用数据是因为它们同时指向了一个对象,但这仍然是按值调用而不是引用调用。38、Java 中的 Math.round(-1.5) 等于多少?

等于 -1,因为在数轴上取值时,中间值(0.5)向右取整,所以正 0.5 是往上取整,负 0.5 是直接舍弃。

39、两个二进制数的异或结果是什么?

两个二进制数异或结果是这两个二进制数差的绝对值。表达式如下:a^b = |a-b|。

两个二进制 a 与 b 异或,即 a 和 b 两个数按位进行运算。如果对应的位相同,则为 0(相当于对应的算术相减),如果不同即为 1(相当于对应的算术相加)。由于二进制每个位只有两种状态,要么是 0,要么是 1,则按位异或操作可表达为按位相减取值相对值,再按位累加。

Error 类: 一般是指与虚拟机相关的问题,如:系统崩溃、虚拟机错误、内存空间不足、方法调用栈溢出等。这类错误将会导致应用程序中断,仅靠程序本身无法恢复和预防;
Exception 类:分为运行时异常和受检查的异常。41、运行时异常与受检异常有何异同?
运行时异常:如:空指针异常、指定的类找不到、数组越界、方法传递参数错误、数据类型转换错误。可以编译通过,但是一运行就停止了,程序不会自己处理; 受检查异常:要么用 try … catch… 捕获,要么用 throws 声明抛出,交给父类处理。42、throw 和 throws 的区别?(1)throw:在方法体内部,表示抛出异常,由方法体内部的语句处理;throw 是具体向外抛出异常的动作,所以它抛出的是一个异常实例; (2)throws:在方法声明后面,表示如果抛出异常,由该方法的调用者来进行异常的处理;表示出现异常的可能性,并不一定会发生这种异常。43、常见的异常类有哪些?
NullPointerException:当应用程序试图访问空对象时,则抛出该异常。 SQLException:提供关于数据库访问错误或其他错误信息的异常。
IndexOutOfBoundsException:指示某排序索引(例如对数组、字符串或向量的排序)超出范围时抛出。
FileNotFoundException:当试图打开指定路径名表示的文件失败时,抛出此异常。
IOException:当发生某种 I/O 异常时,抛出此异常。此类是失败或中断的 I/O 操作生成的异常的通用类。
ClassCastException:当试图将对象强制转换为不是实例的子类时,抛出该异常。 IllegalArgumentException:抛出的异常表明向方法传递了一个不合法或不正确的参数。44、主线程可以捕获到子线程的异常吗?

线程设计的理念:“线程的问题应该线程自己本身来解决,而不要委托到外部”。

正常情况下,如果不做特殊的处理,在主线程中是不能够捕获到子线程中的异常的。如果想要在主线程中捕获子线程的异常,我们可以用如下的方式进行处理,使用 Thread 的静态方法。

泛型是通过类型擦除来实现的,编译器在编译时擦除了所有类型相关的信息,所以在运行时不存在任何类型相关的信息。例如:List<String> 在运行时仅用一个 List 来表示。这样做的目的,是确保能和 Java 5 之前的版本开发二进制类库进行兼容。

类型擦除:泛型信息只存在于代码编译阶段,在进入 JVM 之前,与泛型相关的信息会被擦除掉,专业术语叫做类型擦除。在泛型类被类型擦除的时候,之前泛型类中的类型参数部分如果没有指定上限,如 <T> 则会被转译成普通的 Object 类型,如果指定了上限如 <T extends String> 则类型参数就被替换成类型上限。

2、第一个 String 就是告诉编译器,List 中存储的是 String 对象,也就是起类型检查的作用,之后编译器会擦除泛型占位符,以保证兼容以前的代码。

46、什么是泛型中的限定通配符和非限定通配符 ?

限定通配符对类型进行了限制。有两种限定通配符,一种是<? extends T> 它通过确保类型必须是 T 的子类来设定类型的上界,另一种是<? super T>它通过确保类型必须是 T 的父类来设定类型的下界。泛型类型必须用限定内的类型来进行初始化,否则会导致编译错误。另一方面 <?> 表示了非限定通配符,因为 <?> 可以用任意类型来替代。

Array 不支持泛型,要用 List 代替 Array,因为 List 可以提供编译器的类型安全保证,而 Array却不能。

48、如何实现对象的克隆?
(1)实现 Cloneable 接口并重写 Object 类中的 clone() 方法; (2)实现 Serializable 接口,通过对象的序列化和反序列化实现克隆,可以实现真正的深克隆。

49、深克隆和浅克隆的区别?

(1)浅克隆:拷贝对象和原始对象的引用类型引用同一个对象。浅克隆只是复制了对象的引用地址,两个对象指向同一个内存地址,所以修改其中任意的值,另一个值都会随之变化,这就是浅克隆。 (2)深克隆:拷贝对象和原始对象的引用类型引用不同对象。深拷贝是将对象及值复制过来,两个对象修改其中任意的值另一个值不会改变,这就是深拷贝(例:JSON.parse() 和

深克隆的实现就是在引用类型所在的类实现 Cloneable 接口,并使用 public 访问修饰符重写 clone 方法。 Java 中定义的 clone 没有深浅之分,都是统一的调用 Object 的 clone 方法。 为什么会有深克隆的概念? 是由于我们在实现的过程中刻意的嵌套了 clone 方法的调用。 也就是说深克隆就是在需要克隆的对象类型的类中重新实现克隆方法

对象序列化是一个用于将对象状态转换为字节流的过程,可以将其保存到磁盘文件中或通过网络发送到任何其他程序。从字节流创建对象的相反的过程称为反序列化。而创建的字节流是与平台无关的,在一个平台上序列化的对象可以在不同的平台上反序列化。序列化是为了解决在对象流进行读写操作时所引发的问题。

的对象写出,要恢复的话则使用输入流。

51、什么情况下需要序列化?
(1)当你想把的内存中的对象状态保存到一个文件中或者数据库中时候;
(2)当你想用套接字在网络上传送对象的时候; (3)当你想通过 RMI 传输对象的时候。
52、Java 中的反射是什么意思?有哪些应用场景?

每个类都有一个 Class 对象,包含了与类有关的信息。当编译一个新类时,会产生一个同名的 .class 文件,该文件内容保存着 Class 对象。类加载相当于 Class 对象的加载,类在第一次使用时才动态加载到 JVM 中。也可以使用 Class.forName("com.mysql.jdbc.Driver") 这种方式来控制类的加载,该方法会返回一个 Class

反射可以提供运行时的类信息,并且这个类可以在运行时才加载进来,甚至在编译时期该类的 .class 不存在也可以加载进来。Class 和 java.lang.reflect 一起对反射提供了支持,java.lang.reflect 类库主要包含了以下三个类:

应用举例:工厂模式,使用反射机制,根据全限定类名获得某个类的 Class 实例。53、反射的优缺点?

运行期类型的判断,class.forName() 动态加载类,提高代码的灵活度;

缺点:尽管反射非常强大,但也不能滥用。如果一个功能可以不用反射完成,那么最好就不用。在我们使用反射技术时,下面几条内容应该牢记于心。 (1)性能开销 :反射涉及了动态类型的解析,所以 JVM 无法对这些代码进行优化。因此,反射操作的效率要比那些非反射操作低得多。我们应该避免在经常被执行的代码或对性能要求很高的程序中使用反射。
(2)安全限制 :使用反射技术要求程序必须在一个没有安全限制的环境中运行。如果一个程序必须在有安全限制的环境中运行,如 Applet,那么这就是个问题了。
(3)内部暴露:由于反射允许代码执行一些在正常情况下不被允许的操作(比如:访问私有的属性和方法),所以使用反射可能会导致意料之外的副作用,这可能导致代码功能失调并破坏可移植性。反射代码破坏了抽象性,因此当平台发生改变的时候,代码的行为就有可能也随着变化。
54、Java 中的动态代理是什么?有哪些应用?
动态代理:当想要给实现了某个接口的类中的方法,加一些额外的处理。比如说加日志,加事务等。可以给这个类创建一个代理,故名思议就是创建一个新的类,这个类不仅包含原来类方法的功能,而且还在原来的基础上添加了额外处理的新功能。这个代理类并不是定义好的,是动态生成的。具有解耦意义,灵活,扩展性强。 动态代理的应用:Spring 的 AOP 、加事务、加权限、加日志。55、怎么实现动态代理?

首先必须定义一个接口,还要有一个 InvocationHandler(将实现接口的类的对象传递给它)处理类。再有一个工具类 Proxy(习惯性将其称为代理类,因为调用它的 newInstance() 可以产生代理对象,其实它只是一个产生代理对象的工具类)。利用到 InvocationHandler,拼接代理类源码,将其编译生成代理类的二进制码,利用加载器加载,并将其实例化产生代理对象,最后返回。

每一个动态代理类都必须要实现 InvocationHandler 这个接口,并且每个代理类的实例都关联到了一个 handler,当我们通过代理对象调用一个方法的时候,这个方法的调用就会被转发为由 InvocationHandler 这个接口的 invoke 方法来进行调用。我们来看看 InvocationHandler 这个接口的唯一一个方法 invoke

ClassLoader 对象来对生成的代理对象进行加载; interfaces:一个 Interface 对象的数组,表示的是我将要给我需要代理的对象提供一组什么接口,如果我提供了一组接口给它,那么这个代理对象就宣称实现了该接口(多态),这样我就能调用这组接口中的方法了 handler:一个 InvocationHandler 对象,表示的是当我这个动态代理对象在调用方法的时候,会关联到哪一个 InvocationHandler 对象上。

通过 Proxy.newProxyInstance 创建的代理对象是在 Jvm 运行时动态生成的一个对象,它并不是我们的 InvocationHandler 类型,也不是我们定义的那组接口的类型,而是在运行是动态生成的一个对象。

56、Java 中的 IO 流的分类?说出几个你熟悉的实现类?
按功能来分:输入流(input)、输出流(output)。 按类型来分:字节流 和 字符流。
字节流:InputStream/OutputStream 是字节流的抽象类,这两个抽象类又派生了若干子类,不同的子类分别处理不同的操作类型。具体子类如下所示:

字符流:Reader/Writer 是字符的抽象类,这两个抽象类也派生了若干子类,不同的子类分别处理不同的操作类型。

57、字节流和字符流有什么区别?

字节流按 8 位传输,以字节为单位输入输出数据,字符流按 16 位传输,以字符为单位输入输出数据。

但是不管文件读写还是网络发送接收,信息的最小存储单元都是字节。

BIO:Block IO 同步阻塞式 IO,就是我们平常使用的传统 IO,它的特点是模式简单使用方便,并发处理能力低。同步阻塞I/O模式,数据的读取写入必须阻塞在一个线程内等待其完成。在活动连接数不是特别高(小于单机 1000)的情况下,这种模型是比较不错的,可以让每一个连接专注于自己的 I/O 并且编程模型简单,也不用过多考虑系统的过载、限流等问题。线程池本身就是一个天然的漏斗,可以缓冲一些系统处理不了的连接或请求。但是,当面对十万甚至百万级连接的时候,传统的 BIO 模型是无能为力的。因此,我们需要一种更高效的 I/O 处理模型来应对更高的并发量。 NIO:New IO 同步非阻塞 IO,是传统 IO 的升级,客户端和服务器端通过 Socket 和 ServerSocket 相对应的 SocketChannel 和 ServerSocketChannel 两种不同的套接字通道实现,两种通道都支持阻塞和非阻塞两种模式。阻塞模式使用就像传统中的支持一样,比较简单,但是性能和可靠性都不好;非阻塞模式正好与之相反。对于低负载、低并发的应用程序,可以使用同步阻塞 I/O 来提升开发速率和更好的维护性;对于高负载、高并发的(网络)应用,应使用 NIO AIO:Asynchronous IO 是 NIO 的升级,也叫 NIO2,实现了异步非堵塞 IO ,异步 IO 的操作基于事件和回调机制。也就是应用操作之后会直接返回,不会堵塞在那里,当后台处理完成,操作系统会通知相应的线程进行后续的操作。AIO 是异步 IO 的缩写,虽然 NIO 在网络操作中,提供了非阻塞的方法,但是 NIO 的 IO 行为还是同步的。对于 NIO 来说,我们的业务线程是在 IO 操作准备好时,得到通知,接着就由这个线程自行进行 IO 操作,IO操作本身是同步的。

觉得文章不错的,欢迎点在看转发

公众号后台回复面经获取一份乔哥总结的一份帮助很多公众号粉丝拿到offer的 程序员面试突击手册。

我要回帖

更多关于 代码有哪些 的文章

 

随机推荐