为什么我差不多两年几乎创业板连续两年亏损天天玩游戏都不会近视

当前已经是最后一张
如何让鸽子不甩食
  发现训放迟归的鸽子,几乎不发生甩食的现象,仔细想想可能是因为迟归的鸽子在半路落地休息了,这样就缓解了因为紧张而造成的胃功能失调。就好像们人一样,拿着枪逼着你玩命跑20公里,然后立刻坐下,把桌上的一碗红烧肉吃下去,再喝一碗凉水,你看看你甩不甩,估计王军霞都得甩。
  所以就秋季做了一个实验,发现无论距离多远的训放,鸽子回来只唤其进棚休息,不给任何食物和水,这时鸽子回来后,大多数都会站在栖架上耸毛眯眼。大约一个半小时后,有的鸽子就开始恢复精神,飞到地上来回寻找食物,体力差一点的,在3小时以内也会恢复得差不多。所有的鸽子都不出现耸毛眯眼的情况,时间大概要2-3小时。这时可以适当地喂点清除饲料,给3分饱就可以,水里加点活菌让其自由饮用。等到晚上6点左右,正常给粮,基本就不会发生甩食的情况,而且还要做到每次训放都这样处理。
  记住鸽子训放回来2小时内,绝对不要给食和水,给食就会涨归,给水就会水归,这些都是嗉囊炎诱发的因素。
  用这样的方法,鸽子连续两年都没有发生甩食现象。
  咱们鸽友都很心疼鸽子,鸽子一回来就迫不及待地喂食和水,但这样做反而害了鸽子。所以说甩食是人为造成的,而不是传染造成的。至于有些甩食后会出现死亡和歪脖的,其实都是甩食诱发的疾病。也许鸽子本身就带有这样的病毒,只是健康的时候免疫系统能控制它,一但甩食体质下降,病毒就会显现。
&&来自安卓
&点击添加表情×
星期六&&02/10
积分排行榜
积分 37297
积分 19122
积分 18607
积分 18300
积分 16592
粉丝增长排行榜
可能感兴趣的人
Copyright &
ChinaXinge.com All Rights Reserved
中国.深圳 电话:8、 传真:8
《中华人民共和国增值电信业务经营许可证》为什么我们每天都需要睡觉?一个人最长能连续多久不睡觉?
日常经验分享的时候又到了,今天图钉们有哪些问题呢,下面就让图老师生活小编也给大家介绍睡觉的知识吧,关注健康,珍爱身体!
睡觉经验分享图钉问:为什么我们每天都需要睡觉?一个人最长能连续多久不睡觉?
图老师答:睡眠是机体极为重要的需求,实验证明,老鼠不睡觉会死亡,他们的死因并不是体力上的疲劳,而是因为不能睡眠。
睡眠本质是高级神经活动的抑制作用,是神经系统高级部位发生普遍抑制的表现。当抑制过程在大脑皮层中占优势的时候,即向各部位扩散,随后即进入睡眠状态。睡眠正是大脑皮层由兴奋转化为抑制的结果。
睡眠属于保护性抑制,对人体有很重要的意义。睡眠时各种功能活动都减弱,只维持一些必要的基本功能。睡眠时体内合成代谢大于分解代谢,有利于精神和体力的修复以及能量的存储,所以睡眠可以消除疲劳、弥补损耗,为次日的劳动准备条件。
在病理情况下,睡眠可以保护大脑皮层的神经细胞,有利于防止遭受严重的损伤,所以生病的时候,适当的睡眠是有益的。趴着睡觉有什么坏处
手臂麻木酸痛1、趴着睡觉会导致身体各部位出现不适,如头部长时间枕在手臂上时,手臂的血液循环受阻,神经传导手影响,极易出现手臂麻木,酸痛等症状。
视力模糊2、趴着睡觉时间长了,往往醒来后会出现暂时性的视力模糊,这是因为趴着睡觉时压迫到了眼球,造成眼压过高,长期如此,会使眼球胀大,眼轴增长,从而出现视力模糊的症状。
影响消化功能3、趴着睡觉会影响消化,如果吃了午饭就立刻趴着睡觉,胃的笑话功能很容易受到影响,造成胃部的胀气。而且入睡后人的心率相对变慢,流入大脑的血液也会比平时减少。
头晕、耳鸣4、午饭后较多的血液要进入胃肠道,帮助消化,如果这个时候趴着睡觉还会加重脑部的缺血,最终导致头晕、耳鸣、腿软麻木等症状。
晚上经常做噩梦怎么办?图钉问:晚上经常被噩梦吓醒,为什么我会老是做噩梦呢?我也没做什么坏事啊。。
图老师答:
长期多梦是的典型症状,而做噩梦多半是心里因素引起的。就我身边的朋友一般做噩梦的通常有这几种情况:
喜欢看惊悚片,看完晚上就在梦中回忆了,这属于No&zuo!No die!
处理方法:睡前不要看惊悚片,听听轻柔的音乐。
(2)睡觉坏境
睡觉被子盖的太重,或者睡姿不正确,喜欢将手压在心脏部位。
处理方法:改变睡眠习惯,换一个保暖又比较轻的棉被,睡觉的时候不要将手放在心脏部位。
(3)心理因素
有不愿意面对的心里创伤,梦里面我们是透明的,毫不掩饰的,你可以清晰的看到自己的内心,赤裸的面对不愿意面对的事情。通常你白天不愿意面对,刻意回避但是对你影响又很大的事情,晚上做梦的时候就会很容易窜出来。
处理方法:所谓“日有所思,夜有所梦”,生活中遇到的情感纠葛或者惊恐的时间要学会用正确的心态去面对,逃避是没用的,不管遇到什么事都不要太过较真了,可以找人倾诉,发泄情绪。
有不少人做噩梦的时候都会相信迷信,去敬神拜佛,其实也就是涂个安心,心里负担没了,自然不做噩梦了。
昨晚睡好了吗?秋季睡前八忌,你触犯了几条!
秋季睡前八忌
忌睡前吃东西
我们不提倡睡前吃东西,是因为人体在入睡之后,运动量非常小,胃肠道处于休息状态,吃进去的食物不能够被很好的消化,也导致他们在肠道中休息、发酵、繁殖细菌。 不仅导致睡不好,还容易出现口臭、胃火、等现象。
忌睡前喝茶
睡前不宜喝茶,其实不仅仅是茶,咖啡、功能性饮料、可乐等含有咖啡因的饮品都不适宜晚上喝。因为其中的咖啡因、咖啡碱会刺激大脑的中枢神经系统,容易引起兴奋,难以入睡。同时,咖啡因具有利尿作用,即使成功入睡,也会容易起夜,降低睡眠质量。
忌带气入睡
孔子教育我们“食不言寝不语”,这不仅仅是一种礼仪,更是一种养生方法。睡前多说话容易伤肺气,使人精神兴奋,难以入睡。同时,睡前说话言语不和,容易生气。气伤肝,加速衰老。睡眠不稳,睡眠质量差。
忌开窗睡觉
秋分之后天气由秋热变为秋凉,早晚温差大,需要保暖。开窗睡觉人体容易受凉,导致寒气入侵,在秋季最容易发生腹泻、感冒、咳嗽等症状。
忌微醉入睡
如今人们的夜生活较为丰富,应酬也比较多,常会伴着微醉入睡。据研究,睡前饮酒的人,若出现两次打鼾(即呼吸),每次窒息十几秒钟,不但能使血压升高,还能引起、、心肌梗死,甚至因心脏功能紊乱而猝死。
忌穿的过多
秋季还不是十分寒冷,穿得过多睡觉,一方面不利于血液循环,降低睡眠质量。另一方面,穿得过多容易流汗、不盖被子,同样受凉,引起疾病。
忌开灯睡觉
俗语说“睡觉不点灯,早起头不晕”,反之点灯睡觉则危害健康。首先,开着灯睡觉睡眠会不稳,精力不济。其次,晚上开灯容易使眼睛得不到休息,造成第二天眼部酸涩、肿胀。
我们上面提到的7个睡前禁忌,其实都是为了不熬夜,提高睡眠质量。秦汉时期人们更加重视睡觉,在医书《十问》中提到“一昔不卧,百日不复。”说的是一夜不睡,其带来的危害百天也恢复不了,由此可见熬夜的危害性。
我今年42岁 晚上睡觉爱流汗身上很凉 就是有汗引起的 这是怎么回事啊?图钉问:
我今年42岁,晚上睡觉爱流汗、身上很凉,就是有汗引起的,这是怎么回事啊?
图老师答:
病情分析:
你好,根据您的描述,我分析,分清是肾阴还是肾阳虚。它们共同的症状是腰酸痛。阴虚兼有手脚心发热。阳虚兼有手脚心发凉。
指导意见:
建议阴虚吃六味地黄丸或左归丸。阳虚吃金贵肾气丸。病轻吃成药,重或急于好可以喝汤药。注意用药需在医生指导下服用,不要自己滥用。
为什么睡觉的时候会突然一脚踏空
睡觉踩空是正常行为1、睡觉的时候突然一脚踏空是身体的正常行为。人体的睡眠是由浅到深的,在睡觉的过程中人体的全身肌肉会彻底放松,大脑皮层因为受到抑制,身体各机能处于不稳定的状态,随着入睡,身体心跳减慢、体温下降、呼吸减缓,这个时候脚出现抽动引起踩空是神经组织的一种自我调整,是正常的行为。
睡觉踩空可能是劳累引起2、睡觉的时候出现突然的一脚踩空可能与白天身体过于疲劳有关。特别是身体劳累后肌肉过于疲劳很容易在半睡半醒间肢体出现较大的活=活动性而引起的蹬腿,因为没有进入就会给人以踩空感。
睡觉踩空可能是缺钙3、睡觉的时候出现突然的一脚踩空可能是因为骨钙的缺少引起的。但是要注意如果是因为缺钙引起的肌肉痉挛的话一般会伴随有疼痛感,且是经常性的,如果只是睡觉中偶尔出现的抽动则不是缺钙。最好能够就医进行检查确认。
睡觉踩空与长个无关4、有人说睡觉的时候出现突然的踩空是因为人体在长个。这样的说法是没有任何依据的。睡觉出现踩空是各个年龄段的人都会遇到的,而长高是与骨骼的生长有关,与实在扯不上关系。
晚上经常要在睡前拉好几次尿图钉问:
晚上睡觉前,不由自主的需要多上几次厕所才能安心睡觉,没有其他不适
图老师答:
病情分析:&&&&&&你好,这种情况一考虑是泌尿系统问题,但是一般会伴有尿频急痛等表现,二是考虑心理因素,导致大脑发出排尿指令。&&&&&&指导意见:&&&&&&建议可以去做个泌尿系彩超,有就查,保持心情愉悦,可以试着不上这么多次,直接休息。
怎样让孩子自己睡
和孩子分房睡的最佳时期1、一般来说,孩子5岁时心智发育相对成熟,此时是最佳的分房时期。分房太早,会让孩子没有安全感,太晚又会导致孩子产生心理障碍。父母在此之前就应该做好相关准备,来练习孩子的独立性了。
布置一个孩子喜欢的环境2、分房前,父母应和孩子一起布置孩子的房间或床铺,尽量满足孩子对房间的设想。这些孩子自己精心设计的装饰,可以极大地满足孩子心理上的需要,帮助其顺利渡过分房期。
分房之后给孩子一个缓冲期3、在刚开始和孩子分房睡的时候,可以在孩子入睡前给他讲故事,或等他睡着了之后再关灯离开。然后再逐渐地在孩子没有睡熟之前就离开,但是要让孩子的感觉到你就在附近,慢慢地孩子就会进入自己睡觉的阶段。
给孩子找个安慰物品4、如果孩子需要,可以给他找一个安慰物品。例如,让他抱着妈妈的枕头,或者抱着自己喜欢的娃娃睡觉等。等孩子适应了一个人独睡时,再撤掉安慰物品,切记不要逼迫孩子。
& 搜索“图老师”或者“tulaoshi_com”加关注,每天最新的美食、健康、育儿、美妆、、手工DIY等教程让你一手全掌握。推荐关注!【微信扫描下图可直接关注】
来源:https://www.tulaoshi.com/n/4594.html
点击查看更多与《》相关的文章>>
人类睡眠的时间要占到一天中的三分之一,充足的睡眠才能保证有充足的精力,对于老年人来说,由于体质问题,可能需要更多的睡眠,因此,老年人晚上睡不着觉,可能是您会遇到的问题,下面为您解析其中的原因。
睡眠时间是指人及动物的一种自然生理现象,意识的自然的、通常为有规律的暂时中止,在此期间体力得到恢复的过程时间,能达到闭目安息,大脑皮质处于休息状态。睡眠不足或者熬夜会对身体的危害很大,下面图老师就大家讲一下睡眠时间多少为宜,为了自己的身体快点看看吧!
睡觉多梦怎么办,多梦是人完成睡眠过程后,感觉乱梦纷纭并伴有头晕疲倦的一种状态。
今天图老师为广大图钉们要带来小编精心整理编辑的每天睡多长时间最好,由小编亲身整理编辑的哦,喜欢的朋友多点下赞吧,生活百科频道的小编最苦逼了。
新生儿的护理其实有很多方面要注意,新升级做了父母们的朋友可要多了解些相关知识了。图老师小编在此介绍下一个月的宝宝不睡觉怎么办?,赶紧跟着一起了解下吧!
晚上睡觉时出汗是会影响到我们的睡眠的,特别是一些朋友会出很多的汗,甚至会浸湿枕头和被单,下面就给大家分享一下晚上睡觉会出汗的原因。
热门搜索:
喜乐蒂牧羊犬耳朵固定方法图解教程
生活小常识 了解最伤牙齿的几种行为
阳光彩色瓶的创意生活
传统野钓抖灰面单线双钩钓鲫鱼技巧
婴儿推车简笔画实际来说难度不大,婴儿车由推手座椅及支架滑轮部分组成。今天来学习绘画的是一款复古式的婴儿推车。我们接下来将通过婴儿车简笔画图片教程来学习婴儿车简笔画的画法。
自动高射炮为经典的车载式自动高射炮,有两根炮管,炮管细长专用于应对空中敌人。今天小朋友可以通过学习高射炮简笔画来了解更多军事大炮相关知识。接下来就让我们一起来学习自动高射炮的画法吧!
自动榴弹炮坦克是现代军事武器,与车辆构成一体,越野性能好杀伤范围大。通过学习自动榴弹炮简笔画来了解更多军事知识。今天我们将通过自动榴弹炮儿童简笔画步骤图解来学习自动榴弹炮儿童简笔画的画法。
自动炮是一种现代化军事武器,由炮管炮口炮托等部件构成。小朋友可以学习自动炮简笔画的同时了解更多军事相关知识。接下来我们就通过自动炮简笔画教程图解步骤来学习怎么画自动炮简笔画。
夏天已经到来了,很多人都知道夏天天气炎热,但同时雨伞较多,尤其是在我国南方地区,经常会有暴雨出现。因此,每当到了夏季,空气都非常潮湿。而我们家里的房间内也经常都是湿漉漉的。那么夏天房间潮湿怎么办呢?下面就分享一些夏季房间除湿的好方法。
很多朋友不仅脸上喜欢长痘痘,后背也经常会冒出一些来,一到夏天都不敢穿露背装,可以说是想尽各种办法祛痘痘,但是有时候方法不对反而适得其反,我们在治疗痘痘之前先要了解它为什么会长,从根本治疗,那么后背长痘痘是什么原因呢?
母亲节马上就要到了,为了纪念和感谢生育养育我们的母亲,把每年5月的第二个星期日定为母亲节。对于已婚男士,除了要孝敬自己的母亲之外,可别忘了老婆的母亲,岳母大人哦。那么,母亲节送什么礼物给岳母呢?下面就一起来看看吧。
马上又要到一年一度的女生节了,女生节可是女生最大的节日哦。女生节这天,可以把自己打扮得漂漂亮亮的,画上美丽的妆容让自己的心情更加愉悦。当然,男生可不能闲着啦,在女生节之前就要选好礼物哦。那么,2017年女生节是哪一天,女生节送女生什么礼物好呢?下面就一起来看看吧。
很快就要到母亲节了,从小到大,妈妈把最好的都留给了我们,你有没有准备妈妈的礼物呢?今天就给大家列了一份实用的礼物清单,相信小小的礼物,可以展现你表大大的孝心!下面就给大家来详细介绍吧。
情人节马上就要来临了,这也意外着要准备好情人节礼物了,相信已经让不少男人想破头脑,这份情人节礼物既要让她开心,又要给自己留足面子,还要能把她感动得稀里哗啦,今天就给你们推荐情人节送女友的一些礼物,总有一款适合你。
马上就要到父亲节了,我们都知道这是一个感恩父亲的几日,俗话说“父爱如山”,到了这一天作为父母总应该有所表示,那么,我们应该送什么礼物才能表达出儿女对父亲的那一片心意呢,其实送礼并不难,下面就给大家良心推荐最适合送给父亲的礼物吧。
2017年最受欢迎的生活服务APP!!生活服务类App的出现为日常生活带来极大便利,我们可以通过手机客户端预约各种家政服务,足不出户就能够享受实惠、便捷的生活服务,那么接下来我为大家推荐一些生活中必备生活app。
由于现在抢红包大热起来,很多app都开始支持发红包抢红包了,因此也出现了微信抢红包神器,可以自动抢微信红包,肯定比认为手动操作快,有消息就会自动打开,然后打开红包。今天就给大家分享2017微信自动抢红包神器大全。
2017手机赚钱软件排行榜!!很多朋友都在苦恼每天的钱不够话,空闲时间不知道怎么打发,总想寻找一种简单的赚钱方式,我们的手机app就有很多,下面具体给大家介绍手机赚钱软件有哪些,总有一款你中意的。休闲娱乐生活服务其他类别扫扫有惊喜连续几个月,我几乎每天都不出门,躺在床上,不愿跟别...
患者信息:女
病情描述:连续几个月,我几乎每天都不出门,躺在床上,不愿跟别人说话,害怕出门见到熟人,不想做任何事情,注意力不能集中,不想学习,每天都感觉很疲乏,晚上睡觉两三点才睡着,每天都不开心,有时候想到以前的事情总想哭,觉得人生没有追求,总觉得自己很没用,什么都不如人,吃饭也不规律,也比以前吃得少,
希望解决的问题:我这是怎么了?该怎么办?
最佳回答百姓健康网54775位专家为您在线解答
病情分析:心理分析:这个有点像抑郁症的表现,情绪低落,兴趣减退,意志活动减少,总觉得自己不如人,易哭泣。睡眠质量差!心理指导:因为这种情况持续时间有点长,而且你的具体情况不是很清楚,建议你可以去做个心理咨询,或者去医院看看
回答时间:
Ta帮助了967人
回答列表(2)
Ta帮助了309人
病情分析:心理分析:27岁女士,持续多月缺乏活动兴趣、感觉疲惫、情绪低落、自责等。这是抑郁症的明显表现。心理指导:您好!从您的描述来看,您有非常明显的抑郁表现,比较可能是您患上了抑郁症。这是一种心理疾病,需要您尽快联系心理医生接受当面的心理健康检查来确诊您的情况。您可以联系心理咨询中心、或者医院心理科,找心理医生进行当面的心理健康检查和心理治疗来帮助您的。从您描述的情况来看,程度比较重、比较急,需要您尽早去联系心理医生检查诊治的。
回答时间:
Ta帮助了3304人
病情分析:心理分析:你几个月都不想出门,不想和别人说话,害怕出门见到熟人,不想做事,注意力不能集中,感觉疲乏,不开心,想以前的事总想哭,觉得没有追求,很没用,什么都不如人,饭也吃得少了,是心里有事想不通,有心结没打开,导致你成了这样。心理指导:建议;你找一位心理咨询师咨询一下,把你的心态调好,打开你的心结,把那些不愉快的事都放下,多想好的事,让你的心情快乐起来,多运动,多去户外活动,多和人交流,这样慢慢的就感觉人生有了追求,给你自己定一个目标,向着目标奋斗,给你自己增加自信心。
回答时间:
抹上花开男
30岁提问时间:
病情描述:女性不孕最好的治疗方法。女性不孕最好的治疗方法
医生建议:引起不孕的原因有很多,男女双方的因素都有可能引起不孕,如男性的精子数量不够,活动度差,精液液化时间过长等;女性的阴道炎症、宫颈炎症、内分泌失调、排卵功能异常、肿瘤、子宫内膜异位症、输卵管不通、自身免疫系统等都可以引起不孕。。最好您和爱人同时来检查,医生会根据你们的具体情况,做针对性检查和治疗
Crystal、Maiden男
39岁提问时间:
病情描述:郑州治疗不孕症哪家医院好不怀孕,很长时间了想尽快去看看有人在医院看过不孕吗谁知道哪家医院好郑州治疗不孕症哪家医院好
医生建议:你好,怀孕是受很多因素影响的,这个首先需要查清楚你是因为什么原因导致的不孕症。建议是男女双方都去检查,夫妻同治有利于最快找出病因,缩短治疗时间。比较常见的导致不孕不育的原因有男性的少精,活动度差,精液液化时间过长等;女性的输卵管不通、宫颈炎症、内分泌失调、排卵功能异常等都会导致不孕的。建议到专业孕育医院诊治。
夏天杀手男
22岁提问时间:
病情描述:输卵管积水怎么治输卵管积水怎么治
医生建议:你好,从你描述的情况看,备孕期检查输卵管积水,原因是炎症渗出积聚在不太通畅的输卵管,所以抗生素灌肠,热敷促进药物积液吸收,建议看正规医院的医生,以免枉费不必要的金钱,早日有好消息
45岁提问时间:
病情描述:无精症治疗医院哪家好啊无精症治疗医院哪家好啊?多次检查无精,确诊为无精症,想问下导致无精症的原因是什么?无精症治疗医院哪家好啊?想问下导致无精症的原因是什么?无精症治疗医院哪家好啊?
医生建议:你好,无精症是指精液检查发现没有精子,引起无精症的原因分为两大类:一是睾丸本身功能障碍称为原发性无精子症或非梗阻性无精症;二是睾丸生精功能正常但因输精管道阻塞精子无法排出体外称为梗阻性无精症。建议你这种情况需要到正规的专科医院去确诊,然后针对性去治疗,不要错过最佳的治疗时期。以上是对“无精症治疗医院哪家好啊”这个问题的解答,希望对您有帮助,建议你去正规不孕不育医院做个全面的检查,明确病因,有针对性的治疗,以免错过最佳的治疗时机,使病情加重,治疗的同时,戒烟酒,忌食辛辣刺激性食物。
不羁的风男
49岁提问时间:
病情描述:排卵障碍性不孕症诊断及治疗专家您好请问专家排卵障碍性不孕症诊断及治疗。排卵障碍性不孕症诊断及治疗本人就是想要得到专业人士的回答!(
医生建议:您好不排卵的原因主要有一下几方面:1、下丘脑障碍2、垂体功能障碍3、卵巢功能障碍包括卵巢原发性闭经和继发性闭经前者包括特纳症候群等后者包括卵巢早衰卵巢的器质性损害如放射线照射后功能丧失肿瘤、炎症所致之破坏665898人阅读
面试笔试(19)
程序语言(83)
2013年年底的时候,我看到了网上流传的一个叫做《Java面试题大全》的东西,认真的阅读了以后发现里面的很多题目是重复且没有价值的题目,还有不少的参考答案也是错误的,于是我花了半个月时间对这个所谓的《Java面试大全》进行了全面的修订并重新发布在我的CSDN博客。在修订的过程中,参照了当时JDK最新版本(Java 7)给出了题目的答案和相关代码,去掉了EJB 2.x、JSF等无用内容或过时内容,补充了数据结构和算法、大型网站技术架构、设计模式、UML、Spring MVC等内容并对很多知识点进行了深入的剖析,例如hashCode方法的设计、垃圾收集、并发编程、数据库事务等。当时我甚至希望把面试中经常出现的操作系统、数据库、软件测试等内容也补充进去,但是由于各种原因,最终只整理出了150道面试题。让我欣慰的是,这150道题还是帮助到了很多人,而且在我CSDN博客上的总访问量超过了5万次,最终还被很多网站和个人以原创的方式转载了。最近一年内,用百度搜索"Java面试"我写的这些东西基本上都排在搜索结果的前5名,这让我觉得"亚历山大",因为我写的这些东西一旦不准确就可能误导很多人。2014年的时候我又整理了30道题,希望把之前遗漏的面试题和知识点补充上去,但是仍然感觉挂一漏万,而且Java 8问世后很多新的东西又需要去总结和整理。为此,我不止一次的修改了之前的180题,修改到自己已经感觉有些疲惫或者厌烦了。2014年至今,自己带的学生又有很多走上了Java程序员、Java工程师的工作岗位,他们的面试经验也还没来得及跟大家分享,冥冥之中似乎有一股力量在刺激我要重新写一篇《Java面试题全集》,于是这篇文章就诞生了。请不要责备我把那些出现过的内容又写了一次,因为每次写东西就算是重复的内容,我也需要对编程语言和相关技术进行重新思考,不仅字斟句酌更是力求至臻完美,所以请相信我分享的一定是更新的、更好的、更有益的东西,这些内容也诉说着一个职业程序员和培训师的思想、精神和情感。
1、面向对象的特征有哪些方面?
答:面向对象的特征主要有以下几个方面:
- 抽象:抽象是将一类对象的共同特征总结出来构造类的过程,包括数据抽象和行为抽象两方面。抽象只关注对象有哪些属性和行为,并不关注这些行为的细节是什么。
- 继承:继承是从已有类得到继承信息创建新类的过程。提供继承信息的类被称为父类(超类、基类);得到继承信息的类被称为子类(派生类)。继承让变化中的软件系统有了一定的延续性,同时继承也是封装程序中可变因素的重要手段(如果不能理解请阅读阎宏博士的《Java与模式》或《设计模式精解》中关于桥梁模式的部分)。
- 封装:通常认为封装是把数据和操作数据的方法绑定起来,对数据的访问只能通过已定义的接口。面向对象的本质就是将现实世界描绘成一系列完全自治、封闭的对象。我们在类中编写的方法就是对实现细节的一种封装;我们编写一个类就是对数据和数据操作的封装。可以说,封装就是隐藏一切可隐藏的东西,只向外界提供最简单的编程接口(可以想想普通洗衣机和全自动洗衣机的差别,明显全自动洗衣机封装更好因此操作起来更简单;我们现在使用的智能手机也是封装得足够好的,因为几个按键就搞定了所有的事情)。
- 多态性:多态性是指允许不同子类型的对象对同一消息作出不同的响应。简单的说就是用同样的对象引用调用同样的方法但是做了不同的事情。多态性分为编译时的多态性和运行时的多态性。如果将对象的方法视为对象向外界提供的服务,那么运行时的多态性可以解释为:当A系统访问B系统提供的服务时,B系统有多种提供服务的方式,但一切对A系统来说都是透明的(就像电动剃须刀是A系统,它的供电系统是B系统,B系统可以使用电池供电或者用交流电,甚至还有可能是太阳能,A系统只会通过B类对象调用供电的方法,但并不知道供电系统的底层实现是什么,究竟通过何种方式获得了动力)。方法重载(overload)实现的是编译时的多态性(也称为前绑定),而方法重写(override)实现的是运行时的多态性(也称为后绑定)。运行时的多态是面向对象最精髓的东西,要实现多态需要做两件事:1). 方法重写(子类继承父类并重写父类中已有的或抽象的方法);2). 对象造型(用父类型引用引用子类型对象,这样同样的引用调用同样的方法就会根据子类对象的不同而表现出不同的行为)。
2、访问修饰符public,private,protected,以及不写(默认)时的区别?
类的成员不写访问修饰时默认为default。默认对于同一个包中的其他类相当于公开(public),对于不是同一个包中的其他类相当于私有(private)。受保护(protected)对子类相当于公开,对不是同一包中的没有父子关系的类相当于私有。Java中,外部类的修饰符只能是public或默认,类的成员(包括内部类)的修饰符可以是以上四种。
3、String 是最基本的数据类型吗?
答:不是。Java中的基本数据类型只有8个:byte、short、int、long、float、double、char、boolean;除了基本类型(primitive type),剩下的都是引用类型(reference type),Java 5以后引入的枚举类型也算是一种比较特殊的引用类型。
4、float f=3.4;是否正确?
答:不正确。3.4是双精度数,将双精度型(double)赋值给浮点型(float)属于下转型(down-casting,也称为窄化)会造成精度损失,因此需要强制类型转换float f =(float)3.4; 或者写成float f =3.4F;。
5、short s1 = 1; s1 = s1 + 1;有错吗?short s1 = 1; s1 += 1;有错吗?
答:对于short s1 = 1; s1 = s1 + 1;由于1是int类型,因此s1+1运算结果也是int 型,需要强制转换类型才能赋值给short型。而short s1 = 1; s1 += 1;可以正确编译,因为s1+= 1;相当于s1 = (short)(s1 + 1);其中有隐含的强制类型转换。
6、Java有没有goto?
答:goto 是Java中的保留字,在目前版本的Java中没有使用。(根据James Gosling(Java之父)编写的《The Java Programming Language》一书的附录中给出了一个Java关键字列表,其中有goto和const,但是这两个是目前无法使用的关键字,因此有些地方将其称之为保留字,其实保留字这个词应该有更广泛的意义,因为熟悉C语言的程序员都知道,在系统类库中使用过的有特殊意义的单词或单词的组合都被视为保留字)
7、int和Integer有什么区别?
答:Java是一个近乎纯洁的面向对象编程语言,但是为了编程的方便还是引入了基本数据类型,但是为了能够将这些基本数据类型当成对象操作,Java为每一个基本数据类型都引入了对应的包装类型(wrapper class),int的包装类就是Integer,从Java 5开始引入了自动装箱/拆箱机制,使得二者可以相互转换。
Java 为每个原始类型提供了包装类型:
- 原始类型: boolean,char,byte,short,int,long,float,double
- 包装类型:Boolean,Character,Byte,Short,Integer,Long,Float,Double
class AutoUnboxingTest {
public static void main(String[] args) {
Integer a = new Integer(3);
Integer b = 3;
int c = 3;
System.out.println(a == b);
System.out.println(a == c);
最近还遇到一个面试题,也是和自动装箱和拆箱有点关系的,代码如下所示:
public class Test03 {
public static void main(String[] args) {
Integer f1 = 100, f2 = 100, f3 = 150, f4 = 150;
System.out.println(f1 == f2);
System.out.println(f3 == f4);
如果不明就里很容易认为两个输出要么都是true要么都是false。首先需要注意的是f1、f2、f3、f4四个变量都是Integer对象引用,所以下面的==运算比较的不是值而是引用。装箱的本质是什么呢?当我们给一个Integer对象赋一个int值的时候,会调用Integer类的静态方法valueOf,如果看看valueOf的源代码就知道发生了什么。
public static Integer valueOf(int i) {
if (i &= IntegerCache.low && i &= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
IntegerCache是Integer的内部类,其代码如下所示:
* Cache to support the object identity semantics of autoboxing for values between
* -128 and 127 (inclusive) as required by JLS.
* The cache is initialized on first usage.
The size of the cache
* may be controlled by the {@code -XX:AutoBoxCacheMax=&size&} option.
* During VM initialization, java.lang.Integer.IntegerCache.high property
* may be set and saved in the private system properties in the
* sun.misc.VM class.
private static class IntegerCache {
static final int low = -128;
static final int
static final Integer cache[];
int h = 127;
String integerCacheHighPropValue =
sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
if (integerCacheHighPropValue != null) {
int i = parseInt(integerCacheHighPropValue);
i = Math.max(i, 127);
h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
} catch( NumberFormatException nfe) {
cache = new Integer[(high - low) + 1];
for(int k = 0; k & cache. k++)
cache[k] = new Integer(j++);
assert IntegerCache.high &= 127;
private IntegerCache() {}
简单的说,如果整型字面量的值在-128到127之间,那么不会new新的Integer对象,而是直接引用常量池中的Integer对象,所以上面的面试题中f1==f2的结果是true,而f3==f4的结果是false。
提醒:越是貌似简单的面试题其中的玄机就越多,需要面试者有相当深厚的功力。
8、&和&&的区别?
答:&运算符有两种用法:(1)按位与;(2)逻辑与。&&运算符是短路与运算。逻辑与跟短路与的差别是非常巨大的,虽然二者都要求运算符左右两端的布尔值都是true整个表达式的值才是true。&&之所以称为短路运算是因为,如果&&左边的表达式的值是false,右边的表达式会被直接短路掉,不会进行运算。很多时候我们可能都需要用&&而不是&,例如在验证用户登录时判定用户名不是null而且不是空字符串,应当写为:username != null &&!username.equals(""),二者的顺序不能交换,更不能用&运算符,因为第一个条件如果不成立,根本不能进行字符串的equals比较,否则会产生NullPointerException异常。注意:逻辑或运算符(|)和短路或运算符(||)的差别也是如此。
补充:如果你熟悉JavaScript,那你可能更能感受到短路运算的强大,想成为JavaScript的高手就先从玩转短路运算开始吧。
9、解释内存中的栈(stack)、堆(heap)和方法区(method area)的用法。
答:通常我们定义一个基本数据类型的变量,一个对象的引用,还有就是函数调用的现场保存都使用JVM中的栈空间;而通过new关键字和构造器创建的对象则放在堆空间,堆是垃圾收集器管理的主要区域,由于现在的垃圾收集器都采用分代收集算法,所以堆空间还可以细分为新生代和老生代,再具体一点可以分为Eden、Survivor(又可分为From Survivor和To Survivor)、Tenured;方法区和堆都是各个线程共享的内存区域,用于存储已经被JVM加载的类信息、常量、静态变量、JIT编译器编译后的代码等数据;程序中的字面量(literal)如直接书写的100、"hello"和常量都是放在常量池中,常量池是方法区的一部分,。栈空间操作起来最快但是栈很小,通常大量的对象都是放在堆空间,栈和堆的大小都可以通过JVM的启动参数来进行调整,栈空间用光了会引发StackOverflowError,而堆和常量池空间不足则会引发OutOfMemoryError。
String str = new String("hello");
上面的语句中变量str放在栈上,用new创建出来的字符串对象放在堆上,而"hello"这个字面量是放在方法区的。
补充1:较新版本的Java(从Java 6的某个更新开始)中,由于JIT编译器的发展和"逃逸分析"技术的逐渐成熟,栈上分配、标量替换等优化技术使得对象一定分配在堆上这件事情已经变得不那么绝对了。
补充2:运行时常量池相当于Class文件常量池具有动态性,Java语言并不要求常量一定只有编译期间才能产生,运行期间也可以将新的常量放入池中,String类的intern()方法就是这样的。
看看下面代码的执行结果是什么并且比较一下Java 7以前和以后的运行结果是否一致。
String s1 = new StringBuilder("go")
.append("od").toString()
System.out.println(s1.intern() == s1)
String s2 = new StringBuilder("ja")
.append("va").toString()
System.out.println(s2.intern() == s2)
10、Math.round(11.5) 等于多少?Math.round(-11.5)等于多少?
答:Math.round(11.5)的返回值是12,Math.round(-11.5)的返回值是-11。四舍五入的原理是在参数上加0.5然后进行下取整。
11、switch 是否能作用在byte 上,是否能作用在long 上,是否能作用在String上?
答:在Java 5以前,switch(expr)中,expr只能是byte、short、char、int。从Java 5开始,Java中引入了枚举类型,expr也可以是enum类型,从Java 7开始,expr还可以是字符串(String),但是长整型(long)在目前所有的版本中都是不可以的。
12、用最有效率的方法计算2乘以8?
答: 2 && 3(左移3位相当于乘以2的3次方,右移3位相当于除以2的3次方)。
补充:我们为编写的类重写hashCode方法时,可能会看到如下所示的代码,其实我们不太理解为什么要使用这样的乘法运算来产生哈希码(散列码),而且为什么这个数是个素数,为什么通常选择31这个数?前两个问题的答案你可以自己百度一下,选择31是因为可以用移位和减法运算来代替乘法,从而得到更好的性能。说到这里你可能已经想到了:31 * num 等价于(num && 5) - num,左移5位相当于乘以2的5次方再减去自身就相当于乘以31,现在的VM都能自动完成这个优化。
public class PhoneNumber {
private int areaC
private String lineN
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + areaC
result = prime * result
+ ((lineNumber == null) ? 0 : lineNumber.hashCode());
result = prime * result + ((prefix == null) ? 0 : prefix.hashCode());
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
PhoneNumber other = (PhoneNumber)
if (areaCode != other.areaCode)
return false;
if (lineNumber == null) {
if (other.lineNumber != null)
return false;
} else if (!lineNumber.equals(other.lineNumber))
return false;
if (prefix == null) {
if (other.prefix != null)
return false;
} else if (!prefix.equals(other.prefix))
return false;
return true;
13、数组有没有length()方法?String有没有length()方法?
答:数组没有length()方法,有length 的属性。String 有length()方法。JavaScript中,获得字符串的长度是通过length属性得到的,这一点容易和Java混淆。
14、在Java中,如何跳出当前的多重嵌套循环?
答:在最外层循环前加一个标记如A,然后用break A;可以跳出多重循环。(Java中支持带标签的break和continue语句,作用有点类似于C和C++中的goto语句,但是就像要避免使用goto一样,应该避免使用带标签的break和continue,因为它不会让你的程序变得更优雅,很多时候甚至有相反的作用,所以这种语法其实不知道更好)
15、构造器(constructor)是否可被重写(override)?
答:构造器不能被继承,因此不能被重写,但可以被重载。
16、两个对象值相同(x.equals(y) == true),但却可有不同的hash code,这句话对不对?
答:不对,如果两个对象x和y满足x.equals(y) == true,它们的哈希码(hash code)应当相同。Java对于eqauls方法和hashCode方法是这样规定的:(1)如果两个对象相同(equals方法返回true),那么它们的hashCode值一定要相同;(2)如果两个对象的hashCode相同,它们并不一定相同。当然,你未必要按照要求去做,但是如果你违背了上述原则就会发现在使用容器时,相同的对象可以出现在Set集合中,同时增加新元素的效率会大大下降(对于使用哈希存储的系统,如果哈希码频繁的冲突将会造成存取性能急剧下降)。
补充:关于equals和hashCode方法,很多Java程序都知道,但很多人也就是仅仅知道而已,在Joshua Bloch的大作《Effective Java》(很多软件公司,《Effective Java》、《Java编程思想》以及《重构:改善既有代码质量》是Java程序员必看书籍,如果你还没看过,那就赶紧去买一本吧)中是这样介绍equals方法的:首先equals方法必须满足自反性(x.equals(x)必须返回true)、对称性(x.equals(y)返回true时,y.equals(x)也必须返回true)、传递性(x.equals(y)和y.equals(z)都返回true时,x.equals(z)也必须返回true)和一致性(当x和y引用的对象信息没有被修改时,多次调用x.equals(y)应该得到同样的返回值),而且对于任何非null值的引用x,x.equals(null)必须返回false。实现高质量的equals方法的诀窍包括:1. 使用==操作符检查"参数是否为这个对象的引用";2. 使用instanceof操作符检查"参数是否为正确的类型";3. 对于类中的关键属性,检查参数传入对象的属性是否与之相匹配;4. 编写完equals方法后,问自己它是否满足对称性、传递性、一致性;5. 重写equals时总是要重写hashCode;6. 不要将equals方法参数中的Object对象替换为其他的类型,在重写时不要忘掉@Override注解。
17、是否可以继承String类?
答:String 类是final类,不可以被继承。
补充:继承String本身就是一个错误的行为,对String类型最好的重用方式是关联关系(Has-A)和依赖关系(Use-A)而不是继承关系(Is-A)。
18、当一个对象被当作参数传递到一个方法后,此方法可改变这个对象的属性,并可返回变化后的结果,那么这里到底是值传递还是引用传递?
答:是值传递。Java语言的方法调用只支持参数的值传递。当一个对象实例作为一个参数被传递到方法中时,参数的值就是对该对象的引用。对象的属性可以在被调用过程中被改变,但对对象引用的改变是不会影响到调用者的。C++和C#中可以通过传引用或传输出参数来改变传入的参数的值。在C#中可以编写如下所示的代码,但是在Java中却做不到。
namespace CS01 {
class Program {
public static void swap(ref int x, ref int y) {
int temp =
public static void Main (string[] args) {
int a = 5, b = 10;
swap (ref a, ref b);
Console.WriteLine ("a = {0}, b = {1}", a, b);
说明:Java中没有传引用实在是非常的不方便,这一点在Java 8中仍然没有得到改进,正是如此在Java编写的代码中才会出现大量的Wrapper类(将需要通过方法调用修改的引用置于一个Wrapper类中,再将Wrapper对象传入方法),这样的做法只会让代码变得臃肿,尤其是让从C和C++转型为Java程序员的开发者无法容忍。
19、String和StringBuilder、StringBuffer的区别?
答:Java平台提供了两种类型的字符串:String和StringBuffer/StringBuilder,它们可以储存和操作字符串。其中String是只读字符串,也就意味着String引用的字符串内容是不能被改变的。而StringBuffer/StringBuilder类表示的字符串对象可以直接进行修改。StringBuilder是Java 5中引入的,它和StringBuffer的方法完全相同,区别在于它是在单线程环境下使用的,因为它的所有方面都没有被synchronized修饰,因此它的效率也比StringBuffer要高。
面试题1 - 什么情况下用+运算符进行字符串连接比调用StringBuffer/StringBuilder对象的append方法连接字符串性能更好?
面试题2 - 请说出下面程序的输出。
class StringEqualTest {
public static void main(String[] args) {
String s1 = "Programming"
String s2 = new String("Programming")
String s3 = "Program"
String s4 = "ming"
String s5 = "Program" + "ming"
String s6 = s3 + s4
System.out.println(s1 == s2)
System.out.println(s1 == s5)
System.out.println(s1 == s6)
System.out.println(s1 == s6.intern())
System.out.println(s2 == s2.intern())
补充:解答上面的面试题需要清除两点:1. String对象的intern方法会得到字符串对象在常量池中对应的版本的引用(如果常量池中有一个字符串与String对象的equals结果是true),如果常量池中没有对应的字符串,则该字符串将被添加到常量池中,然后返回常量池中字符串的引用;2. 字符串的+操作其本质是创建了StringBuilder对象进行append操作,然后将拼接后的StringBuilder对象用toString方法处理成String对象,这一点可以用javap -c StringEqualTest.class命令获得class文件对应的JVM字节码指令就可以看出来。
20、重载(Overload)和重写(Override)的区别。重载的方法能否根据返回类型进行区分?
答:方法的重载和重写都是实现多态的方式,区别在于前者实现的是编译时的多态性,而后者实现的是运行时的多态性。重载发生在一个类中,同名的方法如果有不同的参数列表(参数类型不同、参数个数不同或者二者都不同)则视为重载;重写发生在子类与父类之间,重写要求子类被重写方法与父类被重写方法有相同的返回类型,比父类被重写方法更好访问,不能比父类被重写方法声明更多的异常(里氏代换原则)。重载对返回类型没有特殊的要求。
面试题:华为的面试题中曾经问过这样一个问题 - "为什么不能根据返回类型来区分重载",快说出你的答案吧!
21、描述一下JVM加载class文件的原理机制?
答:JVM中类的装载是由类加载器(ClassLoader)和它的子类来实现的,Java中的类加载器是一个重要的Java运行时系统组件,它负责在运行时查找和装入类文件中的类。
由于Java的跨平台性,经过编译的Java源程序并不是一个可执行程序,而是一个或多个类文件。当Java程序需要使用某个类时,JVM会确保这个类已经被加载、连接(验证、准备和解析)和初始化。类的加载是指把类的.class文件中的数据读入到内存中,通常是创建一个字节数组读入.class文件,然后产生与所加载类对应的Class对象。加载完成后,Class对象还不完整,所以此时的类还不可用。当类被加载后就进入连接阶段,这一阶段包括验证、准备(为静态变量分配内存并设置默认的初始值)和解析(将符号引用替换为直接引用)三个步骤。最后JVM对类进行初始化,包括:1)如果类存在直接的父类并且这个类还没有被初始化,那么就先初始化父类;2)如果类中存在初始化语句,就依次执行这些初始化语句。
类的加载是由类加载器完成的,类加载器包括:根加载器(BootStrap)、扩展加载器(Extension)、系统加载器(System)和用户自定义类加载器(java.lang.ClassLoader的子类)。从Java 2(JDK 1.2)开始,类加载过程采取了父亲委托机制(PDM)。PDM更好的保证了Java平台的安全性,在该机制中,JVM自带的Bootstrap是根加载器,其他的加载器都有且仅有一个父类加载器。类的加载首先请求父类加载器加载,父类加载器无能为力时才由其子类加载器自行加载。JVM不会向Java程序提供对Bootstrap的引用。下面是关于几个类加载器的说明:
Bootstrap:一般用本地代码实现,负责加载JVM基础核心类库(rt.jar);
Extension:从java.ext.dirs系统属性所指定的目录中加载类库,它的父加载器是Bootstrap;
System:又叫应用类加载器,其父类是Extension。它是应用最广泛的类加载器。它从环境变量classpath或者系统属性java.class.path所指定的目录中记载类,是用户自定义加载器的默认父加载器。
22、char 型变量中能不能存贮一个中文汉字,为什么?
答:char类型可以存储一个中文汉字,因为Java中使用的编码是Unicode(不选择任何特定的编码,直接使用字符在字符集中的编号,这是统一的唯一方法),一个char类型占2个字节(16比特),所以放一个中文是没问题的。
补充:使用Unicode意味着字符在JVM内部和外部有不同的表现形式,在JVM内部都是Unicode,当这个字符被从JVM内部转移到外部时(例如存入文件系统中),需要进行编码转换。所以Java中有字节流和字符流,以及在字符流和字节流之间进行转换的转换流,如InputStreamReader和OutputStreamReader,这两个类是字节流和字符流之间的适配器类,承担了编码转换的任务;对于C程序员来说,要完成这样的编码转换恐怕要依赖于union(联合体/共用体)共享内存的特征来实现了。
23、抽象类(abstract class)和接口(interface)有什么异同?
答:抽象类和接口都不能够实例化,但可以定义抽象类和接口类型的引用。一个类如果继承了某个抽象类或者实现了某个接口都需要对其中的抽象方法全部进行实现,否则该类仍然需要被声明为抽象类。接口比抽象类更加抽象,因为抽象类中可以定义构造器,可以有抽象方法和具体方法,而接口中不能定义构造器而且其中的方法全部都是抽象方法。抽象类中的成员可以是private、默认、protected、public的,而接口中的成员全都是public的。抽象类中可以定义成员变量,而接口中定义的成员变量实际上都是常量。有抽象方法的类必须被声明为抽象类,而抽象类未必要有抽象方法。
24、静态嵌套类(Static Nested Class)和内部类(Inner Class)的不同?
答:Static Nested Class是被声明为静态(static)的内部类,它可以不依赖于外部类实例被实例化。而通常的内部类需要在外部类实例化后才能实例化,其语法看起来挺诡异的,如下所示。
* 扑克类(一副扑克)
public class Poker {
private static String[] suites = {"黑桃", "红桃", "草花", "方块"};
private static int[] faces = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13};
private Card[]
public Poker() {
cards = new Card[52];
for(int i = 0; i & suites. i++) {
for(int j = 0; j & faces. j++) {
cards[i * 13 + j] = new Card(suites[i], faces[j]);
* 洗牌 (随机乱序)
public void shuffle() {
for(int i = 0, len = cards. i & i++) {
int index = (int) (Math.random() * len);
Card temp = cards[index];
cards[index] = cards[i];
cards[i] =
* index 发牌的位置
public Card deal(int index) {
return cards[index];
* 卡片类(一张扑克)
* [内部类]
public class Card {
private int
public Card(String suite, int face) {
this.suite =
this.face =
public String toString() {
String faceStr = "";
switch(face) {
case 1: faceStr = "A"; break;
case 11: faceStr = "J"; break;
case 12: faceStr = "Q"; break;
case 13: faceStr = "K"; break;
default: faceStr = String.valueOf(face);
return suite + faceS
测试代码:
class PokerTest {
public static void main(String[] args) {
Poker poker = new Poker();
poker.shuffle();
Poker.Card c1 = poker.deal(0);
Poker.Card c2 = poker.new Card("红心", 1);
System.out.println(c1);
System.out.println(c2);
面试题 - 下面的代码哪些地方会产生编译错误?
class Outer {
class Inner {}
public static void foo() { new Inner(); }
public void bar() { new Inner(); }
public static void main(String[] args) {
new Inner();
注意:Java中非静态内部类对象的创建要依赖其外部类对象,上面的面试题中foo和main方法都是静态方法,静态方法中没有this,也就是说没有所谓的外部类对象,因此无法创建内部类对象,如果要在静态方法中创建内部类对象,可以这样做:
new Outer().new Inner();
25、Java 中会存在内存泄漏吗,请简单描述。
答:理论上Java因为有垃圾回收机制(GC)不会存在内存泄露问题(这也是Java被广泛使用于服务器端编程的一个重要原因);然而在实际开发中,可能会存在无用但可达的对象,这些对象不能被GC回收,因此也会导致内存泄露的发生。例如Hibernate的Session(一级缓存)中的对象属于持久态,垃圾回收器是不会回收这些对象的,然而这些对象中可能存在无用的垃圾对象,如果不及时关闭(close)或清空(flush)一级缓存就可能导致内存泄露。下面例子中的代码也会导致内存泄露。
import java.util.A
import java.util.EmptyStackE
public class MyStack&T& {
private T[]
private int size = 0;
private static final int INIT_CAPACITY = 16;
public MyStack() {
elements = (T[]) new Object[INIT_CAPACITY];
public void push(T elem) {
ensureCapacity();
elements[size++] =
public T pop() {
if(size == 0)
throw new EmptyStackException();
return elements[--size];
private void ensureCapacity() {
if(elements.length == size) {
elements = Arrays.copyOf(elements, 2 * size + 1);
上面的代码实现了一个栈(先进后出(FILO))结构,乍看之下似乎没有什么明显的问题,它甚至可以通过你编写的各种单元测试。然而其中的pop方法却存在内存泄露的问题,当我们用pop方法弹出栈中的对象时,该对象不会被当作垃圾回收,即使使用栈的程序不再引用这些对象,因为栈内部维护着对这些对象的过期引用(obsolete reference)。在支持垃圾回收的语言中,内存泄露是很隐蔽的,这种内存泄露其实就是无意识的对象保持。如果一个对象引用被无意识的保留起来了,那么垃圾回收器不会处理这个对象,也不会处理该对象引用的其他对象,即使这样的对象只有少数几个,也可能会导致很多的对象被排除在垃圾回收之外,从而对性能造成重大影响,极端情况下会引发Disk Paging(物理内存与硬盘的虚拟内存交换数据),甚至造成OutOfMemoryError。
26、抽象的(abstract)方法是否可同时是静态的(static),是否可同时是本地方法(native),是否可同时被synchronized修饰?
答:都不能。抽象方法需要子类重写,而静态的方法是无法被重写的,因此二者是矛盾的。本地方法是由本地代码(如C代码)实现的方法,而抽象方法是没有实现的,也是矛盾的。synchronized和方法的实现细节有关,抽象方法不涉及实现细节,因此也是相互矛盾的。
27、阐述静态变量和实例变量的区别。
答:静态变量是被static修饰符修饰的变量,也称为类变量,它属于类,不属于类的任何一个对象,一个类不管创建多少个对象,静态变量在内存中有且仅有一个拷贝;实例变量必须依存于某一实例,需要先创建对象然后通过对象才能访问到它。静态变量可以实现让多个对象共享内存。
补充:在Java开发中,上下文类和工具类中通常会有大量的静态成员。
28、是否可以从一个静态(static)方法内部发出对非静态(non-static)方法的调用?
答:不可以,静态方法只能访问静态成员,因为非静态方法的调用要先创建对象,在调用静态方法时可能对象并没有被初始化。
29、如何实现对象克隆?
答:有两种方式:
1). 实现Cloneable接口并重写Object类中的clone()方法;
2). 实现Serializable接口,通过对象的序列化和反序列化实现克隆,可以实现真正的深度克隆,代码如下。
import java.io.ByteArrayInputS
import java.io.ByteArrayOutputS
import java.io.ObjectInputS
import java.io.ObjectOutputS
import java.io.S
public class MyUtil {
private MyUtil() {
throw new AssertionError();
@SuppressWarnings("unchecked")
public static &T extends Serializable& T clone(T obj) throws Exception {
ByteArrayOutputStream bout = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bout);
oos.writeObject(obj);
ByteArrayInputStream bin = new ByteArrayInputStream(bout.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bin);
return (T) ois.readObject();
下面是测试代码:
import java.io.S
class Person implements Serializable {
private static final long serialVersionUID = -6042305L;
private int
public Person(String name, int age, Car car) {
this.name =
this.age =
this.car =
public String getName() {
public void setName(String name) {
this.name =
public int getAge() {
public void setAge(int age) {
this.age =
public Car getCar() {
public void setCar(Car car) {
this.car =
public String toString() {
return "Person [name=" + name + ", age=" + age + ", car=" + car + "]";
* 小汽车类
class Car implements Serializable {
private static final long serialVersionUID = -7603702L;
private int maxS
public Car(String brand, int maxSpeed) {
this.brand =
this.maxSpeed = maxS
public String getBrand() {
public void setBrand(String brand) {
this.brand =
public int getMaxSpeed() {
return maxS
public void setMaxSpeed(int maxSpeed) {
this.maxSpeed = maxS
public String toString() {
return "Car [brand=" + brand + ", maxSpeed=" + maxSpeed + "]";
class CloneTest {
public static void main(String[] args) {
Person p1 = new Person("Hao LUO", 33, new Car("Benz", 300));
Person p2 = MyUtil.clone(p1);
p2.getCar().setBrand("BYD");
System.out.println(p1);
} catch (Exception e) {
e.printStackTrace();
注意:基于序列化和反序列化实现的克隆不仅仅是深度克隆,更重要的是通过泛型限定,可以检查出要克隆的对象是否支持序列化,这项检查是编译器完成的,不是在运行时抛出异常,这种是方案明显优于使用Object类的clone方法克隆对象。让问题在编译的时候暴露出来总是好过把问题留到运行时。
30、GC是什么?为什么要有GC?
答:GC是垃圾收集的意思,内存处理是编程人员容易出现问题的地方,忘记或者错误的内存回收会导致程序或系统的不稳定甚至崩溃,Java提供的GC功能可以自动监测对象是否超过作用域从而达到自动回收内存的目的,Java语言没有提供释放已分配内存的显示操作方法。Java程序员不用担心内存管理,因为垃圾收集器会自动进行管理。要请求垃圾收集,可以调用下面的方法之一:System.gc() 或Runtime.getRuntime().gc() ,但JVM可以屏蔽掉显示的垃圾回收调用。
垃圾回收可以有效的防止内存泄露,有效的使用可以使用的内存。垃圾回收器通常是作为一个单独的低优先级的线程运行,不可预知的情况下对内存堆中已经死亡的或者长时间没有使用的对象进行清除和回收,程序员不能实时的调用垃圾回收器对某个对象或所有对象进行垃圾回收。在Java诞生初期,垃圾回收是Java最大的亮点之一,因为服务器端的编程需要有效的防止内存泄露问题,然而时过境迁,如今Java的垃圾回收机制已经成为被诟病的东西。移动智能终端用户通常觉得iOS的系统比Android系统有更好的用户体验,其中一个深层次的原因就在于Android系统中垃圾回收的不可预知性。
补充:垃圾回收机制有很多种,包括:分代复制垃圾回收、标记垃圾回收、增量垃圾回收等方式。标准的Java进程既有栈又有堆。栈保存了原始型局部变量,堆保存了要创建的对象。Java平台对堆内存回收和再利用的基本算法被称为标记和清除,但是Java对其进行了改进,采用“分代式垃圾收集”。这种方法会跟Java对象的生命周期将堆内存划分为不同的区域,在垃圾收集过程中,可能会将对象移动到不同区域:
- 伊甸园(Eden):这是对象最初诞生的区域,并且对大多数对象来说,这里是它们唯一存在过的区域。
- 幸存者乐园(Survivor):从伊甸园幸存下来的对象会被挪到这里。
- 终身颐养园(Tenured):这是足够老的幸存对象的归宿。年轻代收集(Minor-GC)过程是不会触及这个地方的。当年轻代收集不能把对象放进终身颐养园时,就会触发一次完全收集(Major-GC),这里可能还会牵扯到压缩,以便为大对象腾出足够的空间。
与垃圾回收相关的JVM参数:
-Xms / -Xmx — 堆的初始大小 / 堆的最大大小
-Xmn — 堆中年轻代的大小
-XX:-DisableExplicitGC — 让System.gc()不产生任何作用
-XX:+PrintGCDetails — 打印GC的细节
-XX:+PrintGCDateStamps — 打印GC操作的时间戳
-XX:NewSize / XX:MaxNewSize — 设置新生代大小/新生代最大大小
-XX:NewRatio — 可以设置老生代和新生代的比例
-XX:PrintTenuringDistribution — 设置每次新生代GC后输出幸存者乐园中对象年龄的分布
-XX:InitialTenuringThreshold / -XX:MaxTenuringThreshold:设置老年代阀值的初始值和最大值
-XX:TargetSurvivorRatio:设置幸存区的目标使用率
31、String s = new String("xyz");创建了几个字符串对象?
答:两个对象,一个是静态区的"xyz",一个是用new创建在堆上的对象。
32、接口是否可继承(extends)接口?抽象类是否可实现(implements)接口?抽象类是否可继承具体类(concrete class)?
答:接口可以继承接口,而且支持多重继承。抽象类可以实现(implements)接口,抽象类可继承具体类也可以继承抽象类。
33、一个".java"源文件中是否可以包含多个类(不是内部类)?有什么限制?
答:可以,但一个源文件中最多只能有一个公开类(public class)而且文件名必须和公开类的类名完全保持一致。
34、Anonymous Inner Class(匿名内部类)是否可以继承其它类?是否可以实现接口?
答:可以继承其他类或实现其他接口,在Swing编程和Android开发中常用此方式来实现事件监听和回调。
35、内部类可以引用它的包含类(外部类)的成员吗?有没有什么限制?
答:一个内部类对象可以访问创建它的外部类对象的成员,包括私有成员。
36、Java 中的final关键字有哪些用法?
答:(1)修饰类:表示该类不能被继承;(2)修饰方法:表示方法不能被重写;(3)修饰变量:表示变量只能一次赋值以后值不能被修改(常量)。
37、指出下面程序的运行结果。
System.out.print("1");
public A() {
System.out.print("2");
class B extends A{
System.out.print("a");
public B() {
System.out.print("b");
public class Hello {
public static void main(String[] args) {
A ab = new B();
ab = new B();
答:执行结果:1a2b2b。创建对象时构造器的调用顺序是:先初始化静态成员,然后调用父类构造器,再初始化非静态成员,最后调用自身构造器。
提示:如果不能给出此题的正确答案,说明之前第21题Java类加载机制还没有完全理解,赶紧再看看吧。
38、数据类型之间的转换:
- 如何将字符串转换为基本数据类型?
- 如何将基本数据类型转换为字符串?
- 调用基本数据类型对应的包装类中的方法parseXXX(String)或valueOf(String)即可返回相应基本类型;
- 一种方法是将基本数据类型与空字符串("")连接(+)即可获得其所对应的字符串;另一种方法是调用String 类中的valueOf()方法返回相应字符串
39、如何实现字符串的反转及替换?
答:方法很多,可以自己写实现也可以使用String或StringBuffer/StringBuilder中的方法。有一道很常见的面试题是用递归实现字符串反转,代码如下所示:
public static String reverse(String originStr) {
if(originStr == null || originStr.length() &= 1)
return originS
return reverse(originStr.substring(1)) + originStr.charAt(0);
40、怎样将GB2312编码的字符串转换为ISO-8859-1编码的字符串?
答:代码如下所示:
String s1 = "你好";
String s2 = new String(s1.getBytes("GB2312"), "ISO-8859-1");
41、日期和时间:
- 如何取得年月日、小时分钟秒?
- 如何取得从日0时0分0秒到现在的毫秒数?
- 如何取得某月的最后一天?
- 如何格式化日期?
问题1:创建java.util.Calendar 实例,调用其get()方法传入不同的参数即可获得参数所对应的值。Java 8中可以使用java.time.LocalDateTimel来获取,代码如下所示。
public class DateTimeTest {
public static void main(String[] args) {
Calendar cal = Calendar.getInstance()
System.out.println(cal.get(Calendar.YEAR))
System.out.println(cal.get(Calendar.MONTH))
System.out.println(cal.get(Calendar.DATE))
System.out.println(cal.get(Calendar.HOUR_OF_DAY))
System.out.println(cal.get(Calendar.MINUTE))
System.out.println(cal.get(Calendar.SECOND))
LocalDateTime dt = LocalDateTime.now()
System.out.println(dt.getYear())
System.out.println(dt.getMonthValue())
System.out.println(dt.getDayOfMonth())
System.out.println(dt.getHour())
System.out.println(dt.getMinute())
System.out.println(dt.getSecond())
问题2:以下方法均可获得该毫秒数。
Calendar.getInstance().getTimeInMillis()
System.currentTimeMillis()
Clock.systemDefaultZone().millis()
问题3:代码如下所示。
Calendar time = Calendar.getInstance()
time.getActualMaximum(Calendar.DAY_OF_MONTH)
问题4:利用java.text.DataFormat 的子类(如SimpleDateFormat类)中的format(Date)方法可将日期格式化。Java 8中可以用java.time.format.DateTimeFormatter来格式化时间日期,代码如下所示。
import java.text.SimpleDateFormat
import java.time.LocalDate
import java.time.format.DateTimeFormatter
import java.util.Date
class DateFormatTest {
public static void main(String[] args) {
SimpleDateFormat oldFormatter = new SimpleDateFormat("yyyy/MM/dd")
Date date1 = new Date()
System.out.println(oldFormatter.format(date1))
DateTimeFormatter newFormatter = DateTimeFormatter.ofPattern("yyyy/MM/dd")
LocalDate date2 = LocalDate.now()
System.out.println(date2.format(newFormatter))
补充:Java的时间日期API一直以来都是被诟病的东西,为了解决这一问题,Java 8中引入了新的时间日期API,其中包括LocalDate、LocalTime、LocalDateTime、Clock、Instant等类,这些的类的设计都使用了不变模式,因此是线程安全的设计。如果不理解这些内容,可以参考我的另一篇文章。
42、打印昨天的当前时刻。
import java.util.Calendar
class YesterdayCurrent {
public static void main(String[] args){
Calendar cal = Calendar.getInstance()
cal.add(Calendar.DATE, -1)
System.out.println(cal.getTime())
在Java 8中,可以用下面的代码实现相同的功能。
import java.time.LocalDateT
class YesterdayCurrent {
public static void main(String[] args) {
LocalDateTime today = LocalDateTime.now();
LocalDateTime yesterday = today.minusDays(1);
System.out.println(yesterday);
43、比较一下Java和JavaSciprt。
答:JavaScript 与Java是两个公司开发的不同的两个产品。Java 是原Sun Microsystems公司推出的面向对象的程序设计语言,特别适合于互联网应用程序开发;而JavaScript是Netscape公司的产品,为了扩展Netscape浏览器的功能而开发的一种可以嵌入Web页面中运行的基于对象和事件驱动的解释性语言。JavaScript的前身是LiveScript;而Java的前身是Oak语言。
下面对两种语言间的异同作如下比较:
- 基于对象和面向对象:Java是一种真正的面向对象的语言,即使是开发简单的程序,必须设计对象;JavaScript是种脚本语言,它可以用来制作与网络无关的,与用户交互作用的复杂软件。它是一种基于对象(Object-Based)和事件驱动(Event-Driven)的编程语言,因而它本身提供了非常丰富的内部对象供设计人员使用。
- 解释和编译:Java的源代码在执行之前,必须经过编译。JavaScript是一种解释性编程语言,其源代码不需经过编译,由浏览器解释执行。(目前的浏览器几乎都使用了JIT(即时编译)技术来提升JavaScript的运行效率)
- 强类型变量和类型弱变量:Java采用强类型变量检查,即所有变量在编译之前必须作声明;JavaScript中变量是弱类型的,甚至在使用变量前可以不作声明,JavaScript的解释器在运行时检查推断其数据类型。
- 代码格式不一样。
补充:上面列出的四点是网上流传的所谓的标准答案。其实Java和JavaScript最重要的区别是一个是静态语言,一个是动态语言。目前的编程语言的发展趋势是函数式语言和动态语言。在Java中类(class)是一等公民,而JavaScript中函数(function)是一等公民,因此JavaScript支持函数式编程,可以使用Lambda函数和闭包(closure),当然Java 8也开始支持函数式编程,提供了对Lambda表达式以及函数式接口的支持。对于这类问题,在面试的时候最好还是用自己的语言回答会更加靠谱,不要背网上所谓的标准答案。
44、什么时候用断言(assert)?
答:断言在软件开发中是一种常用的调试方式,很多开发语言中都支持这种机制。一般来说,断言用于保证程序最基本、关键的正确性。断言检查通常在开发和测试时开启。为了保证程序的执行效率,在软件发布后断言检查通常是关闭的。断言是一个包含布尔表达式的语句,在执行这个语句时假定该表达式为true;如果表达式的值为false,那么系统会报告一个AssertionError。断言的使用如下面的代码所示:
assert(a & 0); // throws an AssertionError if a &= 0
断言可以有两种形式:
assert Expression1;
assert Expression1 : Expression2 ;
Expression1 应该总是产生一个布尔值。
Expression2 可以是得出一个值的任意表达式;这个值用于生成显示更多调试信息的字符串消息。
要在运行时启用断言,可以在启动JVM时使用-enableassertions或者-ea标记。要在运行时选择禁用断言,可以在启动JVM时使用-da或者-disableassertions标记。要在系统类中启用或禁用断言,可使用-esa或-dsa标记。还可以在包的基础上启用或者禁用断言。
注意:断言不应该以任何方式改变程序的状态。简单的说,如果希望在不满足某些条件时阻止代码的执行,就可以考虑用断言来阻止它。
45、Error和Exception有什么区别?
答:Error表示系统级的错误和程序不必处理的异常,是恢复不是不可能但很困难的情况下的一种严重问题;比如内存溢出,不可能指望程序能处理这样的情况;Exception表示需要捕捉或者需要程序进行处理的异常,是一种设计或实现问题;也就是说,它表示如果程序运行正常,从不会发生的情况。
面试题:2005年摩托罗拉的面试中曾经问过这么一个问题“If a process reports a stack overflow run-time error, what’s the most possible cause?”,给了四个选项a. b. write on an
c. recurs d. array index out of boundary. Java程序在运行时也可能会遭遇StackOverflowError,这是一个无法恢复的错误,只能重新修改代码了,这个面试题的答案是c。如果写了不能迅速收敛的递归,则很有可能引发栈溢出的错误,如下所示:
class StackOverflowErrorTest {
public static void main(String[] args) {
main(null);
提示:用递归编写程序时一定要牢记两点:1. 递归公式;2. 收敛条件(什么时候就不再继续递归)。
46、try{}里有一个return语句,那么紧跟在这个try后的finally{}里的代码会不会被执行,什么时候被执行,在return前还是后?
答:会执行,在方法返回调用者前执行。
注意:在finally中改变返回值的做法是不好的,因为如果存在finally代码块,try中的return语句不会立马返回调用者,而是记录下返回值待finally代码块执行完毕之后再向调用者返回其值,然后如果在finally中修改了返回值,就会返回修改后的值。显然,在finally中返回或者修改返回值会对程序造成很大的困扰,C#中直接用编译错误的方式来阻止程序员干这种龌龊的事情,Java中也可以通过提升编译器的语法检查级别来产生警告或错误,Eclipse中可以在如图所示的地方进行设置,强烈建议将此项设置为编译错误。
47、Java语言如何进行异常处理,关键字:throws、throw、try、catch、finally分别如何使用?
答:Java通过面向对象的方法进行异常处理,把各种不同的异常进行分类,并提供了良好的接口。在Java中,每个异常都是一个对象,它是Throwable类或其子类的实例。当一个方法出现异常后便抛出一个异常对象,该对象中包含有异常信息,调用这个对象的方法可以捕获到这个异常并可以对其进行处理。Java的异常处理是通过5个关键词来实现的:try、catch、throw、throws和finally。一般情况下是用try来执行一段程序,如果系统会抛出(throw)一个异常对象,可以通过它的类型来捕获(catch)它,或通过总是执行代码块(finally)来处理;try用来指定一块预防所有异常的程序;catch子句紧跟在try块后面,用来指定你想要捕获的异常的类型;throw语句用来明确地抛出一个异常;throws用来声明一个方法可能抛出的各种异常(当然声明异常时允许无病呻吟);finally为确保一段代码不管发生什么异常状况都要被执行;try语句可以嵌套,每当遇到一个try语句,异常的结构就会被放入异常栈中,直到所有的try语句都完成。如果下一级的try语句没有对某种异常进行处理,异常栈就会执行出栈操作,直到遇到有处理这种异常的try语句或者最终将异常抛给JVM。
48、运行时异常与受检异常有何异同?
答:异常表示程序运行过程中可能出现的非正常状态,运行时异常表示虚拟机的通常操作中可能遇到的异常,是一种常见运行错误,只要程序设计得没有问题通常就不会发生。受检异常跟程序运行的上下文环境有关,即使程序设计无误,仍然可能因使用的问题而引发。Java编译器要求方法必须声明抛出可能发生的受检异常,但是并不要求必须声明抛出未被捕获的运行时异常。异常和继承一样,是面向对象程序设计中经常被滥用的东西,在Effective Java中对异常的使用给出了以下指导原则:
- 不要将异常处理用于正常的控制流(设计良好的API不应该强迫它的调用者为了正常的控制流而使用异常)
- 对可以恢复的情况使用受检异常,对编程错误使用运行时异常
- 避免不必要的使用受检异常(可以通过一些状态检测手段来避免异常的发生)
- 优先使用标准的异常
- 每个方法抛出的异常都要有文档
- 保持异常的原子性
- 不要在catch中忽略掉捕获到的异常
49、列出一些你常见的运行时异常?
- ArithmeticException(算术异常)
- ClassCastException (类转换异常)
- IllegalArgumentException (非法参数异常)
- IndexOutOfBoundsException (下标越界异常)
- NullPointerException (空指针异常)
- SecurityException (安全异常)
50、阐述final、finally、finalize的区别。
- final:修饰符(关键字)有三种用法:如果一个类被声明为final,意味着它不能再派生出新的子类,即不能被继承,因此它和abstract是反义词。将变量声明为final,可以保证它们在使用中不被改变,被声明为final的变量必须在声明时给定初值,而在以后的引用中只能读取不可修改。被声明为final的方法也同样只能使用,不能在子类中被重写。
- finally:通常放在try…catch…的后面构造总是执行代码块,这就意味着程序无论正常执行还是发生异常,这里的代码只要JVM不关闭都能执行,可以将释放外部资源的代码写在finally块中。
- finalize:Object类中定义的方法,Java中允许使用finalize()方法在垃圾收集器将对象从内存中清除出去之前做必要的清理工作。这个方法是由垃圾收集器在销毁对象时调用的,通过重写finalize()方法可以整理系统资源或者执行其他清理工作。
51、类ExampleA继承Exception,类ExampleB继承ExampleA。
有如下代码片断:
throw new ExampleB("b")
} catch(ExampleA e){
System.out.println("ExampleA");
} catch(Exception e){
System.out.println("Exception");
请问执行此段代码的输出是什么?
答:输出:ExampleA。(根据里氏代换原则[能使用父类型的地方一定能使用子类型],抓取ExampleA类型异常的catch块能够抓住try块中抛出的ExampleB类型的异常)
面试题 - 说出下面代码的运行结果。(此题的出处是《Java编程思想》一书)
class Annoyance extends Exception {}
class Sneeze extends Annoyance {}
class Human {
public static void main(String[] args)
throws Exception {
throw new Sneeze();
catch ( Annoyance a ) {
System.out.println("Caught Annoyance");
catch ( Sneeze s ) {
System.out.println("Caught Sneeze");
System.out.println("Hello World!");
52、List、Set、Map是否继承自Collection接口?
答:List、Set 是,Map 不是。Map是键值对映射容器,与List和Set有明显的区别,而Set存储的零散的元素且不允许有重复元素(数学中的集合也是如此),List是线性结构的容器,适用于按数值索引访问元素的情形。
53、阐述ArrayList、Vector、LinkedList的存储性能和特性。
答:ArrayList 和Vector都是使用数组方式存储数据,此数组元素数大于实际存储的数据以便增加和插入元素,它们都允许直接按序号索引元素,但是插入元素要涉及数组元素移动等内存操作,所以索引数据快而插入数据慢,Vector中的方法由于添加了synchronized修饰,因此Vector是线程安全的容器,但性能上较ArrayList差,因此已经是Java中的遗留容器。LinkedList使用双向链表实现存储(将内存中零散的内存单元通过附加的引用关联起来,形成一个可以按序号索引的线性结构,这种链式存储方式与数组的连续存储方式相比,内存的利用率更高),按序号索引数据需要进行前向或后向遍历,但是插入数据时只需要记录本项的前后项即可,所以插入速度较快。Vector属于遗留容器(Java早期的版本中提供的容器,除此之外,Hashtable、Dictionary、BitSet、Stack、Properties都是遗留容器),已经不推荐使用,但是由于ArrayList和LinkedListed都是非线程安全的,如果遇到多个线程操作同一个容器的场景,则可以通过工具类Collections中的synchronizedList方法将其转换成线程安全的容器后再使用(这是对装潢模式的应用,将已有对象传入另一个类的构造器中创建新的对象来增强实现)。
补充:遗留容器中的Properties类和Stack类在设计上有严重的问题,Properties是一个键和值都是字符串的特殊的键值对映射,在设计上应该是关联一个Hashtable并将其两个泛型参数设置为String类型,但是Java API中的Properties直接继承了Hashtable,这很明显是对继承的滥用。这里复用代码的方式应该是Has-A关系而不是Is-A关系,另一方面容器都属于工具类,继承工具类本身就是一个错误的做法,使用工具类最好的方式是Has-A关系(关联)或Use-A关系(依赖)。同理,Stack类继承Vector也是不正确的。Sun公司的工程师们也会犯这种低级错误,让人唏嘘不已。
54、Collection和Collections的区别?
答:Collection是一个接口,它是Set、List等容器的父接口;Collections是个一个工具类,提供了一系列的静态方法来辅助容器操作,这些方法包括对容器的搜索、排序、线程安全化等等。
55、List、Map、Set三个接口存取元素时,各有什么特点?
答:List以特定索引来存取元素,可以有重复元素。Set不能存放重复元素(用对象的equals()方法来区分元素是否重复)。Map保存键值对(key-value pair)映射,映射关系可以是一对一或多对一。Set和Map容器都有基于哈希存储和排序树的两种实现版本,基于哈希存储的版本理论存取时间复杂度为O(1),而基于排序树版本的实现在插入或删除元素时会按照元素或元素的键(key)构成排序树从而达到排序和去重的效果。
56、TreeMap和TreeSet在排序时如何比较元素?Collections工具类中的sort()方法如何比较元素?
答:TreeSet要求存放的对象所属的类必须实现Comparable接口,该接口提供了比较元素的compareTo()方法,当插入元素时会回调该方法比较元素的大小。TreeMap要求存放的键值对映射的键必须实现Comparable接口从而根据键对元素进行排序。Collections工具类的sort方法有两种重载的形式,第一种要求传入的待排序容器中存放的对象比较实现Comparable接口以实现元素的比较;第二种不强制性的要求容器中的元素必须可比较,但是要求传入第二个参数,参数是Comparator接口的子类型(需要重写compare方法实现元素的比较),相当于一个临时定义的排序规则,其实就是通过接口注入比较元素大小的算法,也是对回调模式的应用(Java中对函数式编程的支持)。
public class Student implements Comparable&Student& {
private int
public Student(String name, int age) {
this.name =
this.age =
public String toString() {
return "Student [name=" + name + ", age=" + age + "]";
public int compareTo(Student o) {
return this.age - o.
import java.util.S
import java.util.TreeS
class Test01 {
public static void main(String[] args) {
Set&Student& set = new TreeSet&&();
set.add(new Student("Hao LUO", 33));
set.add(new Student("XJ WANG", 32));
set.add(new Student("Bruce LEE", 60));
set.add(new Student("Bob YANG", 22));
for(Student stu : set) {
System.out.println(stu);
public class Student {
private int
public Student(String name, int age) {
this.name =
this.age =
* 获取学生姓名
public String getName() {
* 获取学生年龄
public int getAge() {
public String toString() {
return "Student [name=" + name + ", age=" + age + "]";
import java.util.ArrayL
import java.util.C
import java.util.C
import java.util.L
class Test02 {
public static void main(String[] args) {
List&Student& list = new ArrayList&&();
list.add(new Student("Hao LUO", 33));
list.add(new Student("XJ WANG", 32));
list.add(new Student("Bruce LEE", 60));
list.add(new Student("Bob YANG", 22));
Collections.sort(list, new Comparator&Student& () {
public int compare(Student o1, Student o2) {
return o1.getName().compareTo(o2.getName());
for(Student stu : list) {
System.out.println(stu);
57、Thread类的sleep()方法和对象的wait()方法都可以让线程暂停执行,它们有什么区别?
答:sleep()方法(休眠)是线程类(Thread)的静态方法,调用此方法会让当前线程暂停执行指定的时间,将执行机会(CPU)让给其他线程,但是对象的锁依然保持,因此休眠时间结束后会自动恢复(线程回到就绪状态,请参考第66题中的线程状态转换图)。wait()是Object类的方法,调用对象的wait()方法导致当前线程放弃对象的锁(线程暂停执行),进入对象的等待池(wait pool),只有调用对象的notify()方法(或notifyAll()方法)时才能唤醒等待池中的线程进入等锁池(lock pool),如果线程重新获得对象的锁就可以进入就绪状态。
补充:可能不少人对什么是进程,什么是线程还比较模糊,对于为什么需要多线程编程也不是特别理解。简单的说:进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,是操作系统进行资源分配和调度的一个独立单位;线程是进程的一个实体,是CPU调度和分派的基本单位,是比进程更小的能独立运行的基本单位。线程的划分尺度小于进程,这使得多线程程序的并发性高;进程在执行时通常拥有独立的内存单元,而线程之间可以共享内存。使用多线程的编程通常能够带来更好的性能和用户体验,但是多线程的程序对于其他程序是不友好的,因为它可能占用了更多的CPU资源。当然,也不是线程越多,程序的性能就越好,因为线程之间的调度和切换也会浪费CPU时间。时下很时髦的就采用了单线程异步I/O的工作模式。
58、线程的sleep()方法和yield()方法有什么区别?
① sleep()方法给其他线程运行机会时不考虑线程的优先级,因此会给低优先级的线程以运行的机会;yield()方法只会给相同优先级或更高优先级的线程以运行的机会;
② 线程执行sleep()方法后转入阻塞(blocked)状态,而执行yield()方法后转入就绪(ready)状态;
③ sleep()方法声明抛出InterruptedException,而yield()方法没有声明任何异常;
④ sleep()方法比yield()方法(跟操作系统CPU调度相关)具有更好的可移植性。
59、当一个线程进入一个对象的synchronized方法A之后,其它线程是否可进入此对象的synchronized方法B?
答:不能。其它线程只能访问该对象的非同步方法,同步方法则不能进入。因为非静态方法上的synchronized修饰符要求执行方法时要获得对象的锁,如果已经进入A方法说明对象锁已经被取走,那么试图进入B方法的线程就只能在等锁池(注意不是等待池哦)中等待对象的锁。
60、请说出与线程同步以及线程调度相关的方法。
- wait():使一个线程处于等待(阻塞)状态,并且释放所持有的对象的锁;
- sleep():使一个正在运行的线程处于睡眠状态,是一个静态方法,调用此方法要处理InterruptedException异常;
- notify():唤醒一个处于等待状态的线程,当然在调用此方法的时候,并不能确切的唤醒某一个等待状态的线程,而是由JVM确定唤醒哪个线程,而且与优先级无关;
- notityAll():唤醒所有处于等待状态的线程,该方法并不是将对象的锁给所有线程,而是让它们竞争,只有获得锁的线程才能进入就绪状态;
提示:关于Java多线程和并发编程的问题,建议大家看我的另一篇文章。
补充:Java 5通过Lock接口提供了显式的锁机制(explicit lock),增强了灵活性以及对线程的协调。Lock接口中定义了加锁(lock())和解锁(unlock())的方法,同时还提供了newCondition()方法来产生用于线程之间通信的Condition对象;此外,Java 5还提供了信号量机制(semaphore),信号量可以用来限制对某个共享资源进行访问的线程的数量。在对资源进行访问之前,线程必须得到信号量的许可(调用Semaphore对象的acquire()方法);在完成对资源的访问后,线程必须向信号量归还许可(调用Semaphore对象的release()方法)。
下面的例子演示了100个线程同时向一个银行账户中存入1元钱,在没有使用同步机制和使用同步机制情况下的执行情况。
银行账户类:
* 银行账户
public class Account {
private double
* money 存入金额
public void deposit(double money) {
double newBalance = balance +
Thread.sleep(10);
catch(InterruptedException ex) {
ex.printStackTrace();
balance = newB
* 获得账户余额
public double getBalance() {
存钱线程类:
* 存钱线程
public class AddMoneyThread implements Runnable {
private double
public AddMoneyThread(Account account, double money) {
this.account =
this.money }

我要回帖

更多关于 连续两年犯太岁 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信