吉他每天必练的基本功功是每个都要练么

练好基本功,招数才能通(经典实用!)
我的图书馆
练好基本功,招数才能通(经典实用!)
今天有读者咨询“如何做好医药销售”晨老师确实不是做销售的,只给了一些简单的建议下班后,我又查阅了一些资料,结合之前跟销售朋友的交流,简单总结了一下万事万物都有其发展的客观规律,销售也一样,先打好基础再寻求奇招,这才是符合规律的做法下文仅供参考,欢迎销售高手给予指点【选门派】一入销售深似海……咦?搞得跟我做过销售似的,嘿嘿,其实不管做哪项工作都是“深似海”,选个好门派,即行业、公司、产品,尤其重要。建议用以下原则进行筛选:自己是否喜欢,喜欢的方向才有可能深入,能深入才有前途;发展前景好不好,发展前景好你会有更多的机会,机会多成长会更快;家里是否有资源可以帮你,不要任性,家里如果有资源最好能利用,会事半功倍。下面详细说一下行业、公司、产品该怎么选。选行业1、看国家政策导向国家鼓励的方向,一般不会太差。比如近期炒的火热的互联网+,不一定马上要转身去混互联网,但是去做传统行业与互联网结合的事情,应该是不错的选择。2、看市场规模总体市场规模是否够大,是否能持久发展。比如跟民生相关的衣、食、住、行、医疗,总体规模一定够大。但是你要再看细分的行业领域。比如药品销售,目前国家对药品的管制越来越严格,晨老师个人认为,其前景应该不会像前几年那样火爆了。3、看新生的替代行业新生的替代行业是否会让老的行业处于衰退状态,如果是,进入老的行业就要谨慎。比如电信行业,前几年非常火热,但这两年在用户领域逐渐被互联网替代。如果现在进入用户领域的电信行业,就会比较艰难。选公司1、看公司团队公司的创始人,也就是高管团队是什么来历、什么性格、什么价值观。人永远是最主要的因素,跟个好老板真能让你少奋斗几年。2、看业务发展公司目前的业务总量不一定要很高,但发展一定要呈上升趋势。业务发展好,一切都不是问题,业务发展不好,一切都是问题。3、看资本健康度公司的资本结构是否健康,是否会出现阻碍公司发展的局面。资本是公司发展的基石,如果市场一有波动就连工资都发不出来,你的日子可能会比较惨。选产品1、选有良心的这个不解释,起码每天要能睡得着觉吧。2、选对用户有价值的用户是否真实需要这个产品,这个产品对用户是否确实有真实的价值,最好不要做忽悠人的东西,最终受伤害的还是自己。3、选有竞争力的当然,很多情况竞争力就在于销售,是你之后要做的工作。但至少产品本身要说的过去,你在这个基础上才比较好发挥。【修内功】销售是一项与人交往的工作,吃吃喝喝、嘻嘻哈哈只是表面现象,搞定销售目标,要先从提高个人能力素质开始。以下就是销售、乃至任何一个岗位都需要修炼的内功。学习能力工作以后的再学习能力决定了你的发展高度,上面说的选行业、选公司、选产品,以及下面要谈的外功修炼,都需要基础的学习能力。互联网给我们提供了很好的学习环境,90%的学习资源都可以在网上找得到,积累了一定基础,再去线下找师傅、同事、同行交流,补充剩余的细节。1、互联网方式百度,知乎,专业论坛比如你想了解医药行业的发展,可以去百度或知乎直接搜“医药行业的发展”;搜到一些信息后,沿着这条线索再筛选出几个关键词,继续搜这几个关键词……如此多来几轮,你的信息就织成一张网了。然后再去医药行业的专业论坛,看从业者的讨论和经验总结,把这些信息补充道你的“网”里。其他相关知识的学习也是类似的方法。微博,微信公众号比如做医药行业,可以关注医药行业的微博以及公众号。怎么找到他们?微博和微信都有搜索功能,搜“医药”以及相关的关键词,能搜出很多,不用全部关注,筛选5-10个精品即可。每天用业余时间去看看他们推送的内容,长期积累能了解最新信息、学习知识、吸收思想。微信,QQ可以加一些专业的微信群、QQ群,尽量选择以分享为主不闲聊的群,不要让这些碎片化的聊天吞噬了你的时间。在群里遇到高手的话,主动加为好友,在你有足够基础积累的情况下再去与他们深度交流。2、线下方式身边的高手先把身边的资源吃透,你的师父、前辈、同事、同行,自己做好功课以后,去找他们多交流经验心得。交流完之后,最好回去整理成笔记,经常翻出来看看。行业聚会现在各行各业都在搞所谓的“社群”,可以去参加,认识些高手。但是跟线上群一样,先做好自己的积累,再去与高手交流,不要受困于表面的社交游戏中。主动分享把你所学、所感,总结出来,与身边的人、行业社群的人分享。分享是最好的学习方式,开始做的不好不要怕,勇敢去做一定会越做越好。同时,通过分享,会获得更多与高手交流的机会。个人修养专业知识通过上面讲的学习方法,快速补充行业、专业的基础知识,并在后续的工作中不断深入研究。销售工作,不成为业务专家很难做到太高的位置。兴趣爱好培养自己的兴趣爱好,做一个“有意思”的人,大家会跟你愉快的交往。为人处世完善为人处世的方式,修正自己身上不好的行为和习惯。让大家感觉跟你合作很舒服,你就成功一半了。如何修炼?多看哲学、心理学、经济学、文学、艺术等各方面的书籍。不要找“没时间”的借口,逼自己每天必须看几页书,慢慢积累总会有提高;多与形形色色的人交流,多观察别人优点,无论是大学教授还是路边摆摊的小贩,他们身上都有值得你学习的经验和品质。【修外功】下面是晨老师搜集的一些销售技巧,既然是技巧,还得实际操作成为自己的能力才行哦。角色分析1、客户角色使用人使用你产品的人,你要了解他们的使用需求,面对他们重点要用通俗的语言介绍产品的卖点,能给他们带来什么好处。技术专家技术专家会评定你产品的各项功能及性能指标,面对他们重点要用专业的语言展示你们与竞品相比的优势。决策人决策人更关系你的产品能给他们组织,甚至是他个人带来什么好处,重点要介绍使用产品后对组织产生的结果,对决策人个人业绩带来的帮助。注:晨老师不提倡用利益输送的方式搞定决策人,长远看,只会给自己给客户带来不必要的麻烦。决策人身边的人可能是决策人的上级领导,可能是决策人的亲信下属,可能是决策人的亲属朋友。找到这些人,给他们提供你能力范围内的帮助,有时候甚至会成为关键的因素。2、公司内部角色搭档可能是你的平级同事,可能是你的上级,你要站在操盘者的角度,把他们当成资源去调度。技术支持团队与技术支持团队搞好关系,在应对客户“技术专家”时,带上他们一起去公关,比你自己硬碰硬要强很多。行政服务团队人力资源、行政、财务等公司内部的“衙门”,一定要处好关系,做好了可能会给你带来意外的惊喜,做不好也可能给你带来意外的惊吓。3、竞争对手多认识一些竞争对手的同行,一方面可以交流行业经验,另一方面也可能获取意想不到的有用信息。实操技巧最后才是大家常挂在嘴上的所谓“销售技巧”,比如陌生客户拜访、寒暄话术、谈判技巧、逼单技巧等等。晨老师毕竟不是销售,这些技巧网上也都搜得到,就不展开细说啦。您可以按照上面说的学习方法,找个小本子,结合实践经验记录一本属于自己的实操秘籍。在知乎搜索“销售技巧”有249个精华回答本文由管理晨读原创(微信:guanlichendu)欢迎转载,恳请注明来源及微信号
馆藏&17943
TA的最新馆藏[转]&
喜欢该文的人也喜欢Java的基本功:少侠,一定要学好这些 - 简书
Java的基本功:少侠,一定要学好这些
字数 10321
不知道大家有没有这样一种感觉,程序员的数量井喷了。可能是因为互联网火了,也可能是各家培训机构为我们拉来了大量的同行,更有可能是各行各业都不景气了,而互联网(在这前一段时间中)真是红遍了大江南北,烧红了整片天,所以到处都是我们的同行,所以竞争更加激烈了,那么我们就更得稳扎稳打,不能再过那种:“我会用就好了”的日子了,我们必须刨根问底,明白我们赖以生存的编程语言到底是怎么工作的。
1. Java数组与内存控制
一、Java数组初始化
Java数组是静态的,即当数组被初始化之后,该数组的长度是不可变的。Java数组使用之前必须先对数组对象进行初始化,所谓初始化,就是为数组的所有元素分配内存空间,并为每个数组元素指定初始值。
Java基本类型数组的两种初始化方式
静态初始化:初始化时由程序员显式指定每个数组元素的初始值,由系统决定数组长度。
动态初始化:初始化时程序员只指定数组长度,由系统为数组元素分配初始值。
不要同时使用静态初始化和动态初始化,也就是说,不要在进行数组初始化时,既指定数组的长度,也为每个数组元素分配初始值。
Java的数组是静态的,一旦数组初始化完成,数组元素的内存空间分配即结束,程序只能改变数组元素的值,而无法改变数组的长度。Java的数组变量是一种引用类型的变量,数组变量并不是数组本身,它只是指向堆内存中的数组对象。因此,可以改变一个数组变量所引用的数组,这样可以造成数组长度可变的假象。
二:Java数组内存存放
对于基本类型数组而言,数组元素的值直接存储在对应的数组元素中,因此基本类型数组的初始化比较简单:程序直接先为数组分配内存空间,再将数组元素的值存入对应内存里。
所有局部变量都是存放在栈内存里保存的,不管其是基本类型的变量,还是引用类型的变量,都是存储在各自的方法栈区中;但引用类型变量所引用的对象(数组、普通Java对象)则总是存储在堆内存中。
对于很多Java程序员而言,他们最容易混淆的是:引用类型变量何时只是栈内存中的变量本身,何时又变为引用实际的Java对象。其实规则很简单:引用变量本质上只是一个指针,只要程序通过引用变量访问属性,或者通过调用引用变量来调用方法,该引用变量将会由它所引用的对象代替。
三:使用数组
当数组引用变量指向一个有效的数组对象之后,程序就可以通过该数组引用变量来访问数组对象。Java语言不允许直接访问堆内存中的数据,因此无法直接访问堆内存中的数组对象,程序将通过数组引用变量来访问数组。
2.Java内存回收机制
一、Java对象在内存引用状态
内存泄露:程序运行过程中,会不断分配内存空间,那些不再使用的内存空间应该即时回收它们,从而保证系统可以再次使用这些内存,如果存在无用的内存没有被回收回来,这就是内存泄漏。
(1)强引用  这是java程序中最常见的引用方式,程序创建一个对象,并把这个对象赋给一个引用变量,这个引用变量就是强引用.java程序可通过强引用来访问实际的对象。当一个对象被一个或一个以上的强引用变量引用时,它处于可达状态,它不可能被系统垃圾回收机制回收。  强引用是Java编程中广泛使用的引用类型,被强引用所引用的Java对象绝不会被垃圾回收机制回收,即使系统内存紧张;即使有些Java对象以后永远也不会被用到,JVM也不会回收被强引用所引用的Java对象.  由于JVM肯定不会回收强引用所引用的JAVA对象,因此强引用是造成JAVA内存泄漏的主要原因。    如 ReceiptBean rb=new ReceiptBean(); rb就代表了一种强引用的方式。
(2)软引用  软引用需要通过SoftReference类来实现,当一个对象只具有软引用时,它可能被垃圾回收机制回收。对于只有软引用的对象而言,当系统内存空间足够时,它不会被系统回收,程序也可以使用该对象;当系统内存空间不足时,系统将回收它.  软引用通常用在对内存敏感的程序中,软引用是强引用很好的替代。对于软引用,当系统内存空间充足时,软引用与强引用没有太大的区别,当系统内存空间不足时,被软引用所引用的JAVA对象可以被垃圾回收机制回收,从而避免系统内存不足的异常.  当程序需要大量创建某个类的新对象,而且有可能重新访问已创建老对象时,可以充分使用软引用来解决内存紧张的问题。例如需要访问1000个Person对象,可以有两种方式方法一 依次创建1000个对象,但只有一个Person引用指向最后一个Person对象。方法二 定义一个长度为1000个的Person数组,每个数组元素引用一个Person对象。对于方法一,弱点很明显,程序不允许需要重新访问前面创建的Person对象,即使这个对象所占的空间还没有被回收。但已经失去了这个对象的引用,因此也不得不重新创建一个新的Person对象(重新分配内存),而那个已有的Person对象(完整的,正确的,可用的)则只能等待垃圾回收。对于方法二,优势是可以随时重新访问前面创建的每个Person对象,但弱点也有,如果系统堆内存空间紧张,而1000个Person对象都被强引用引着,垃圾回收机制也不可能回收它们的堆内存空间,系统性能将变成非常差,甚至因此内存不足导致程序中止。  如果用软引用则是一种较好的方案,当堆内存空间足够时,垃圾回收机制不会回收Person对象,可以随时重新访问一个已有的Person对象,这和普通的强引用没有任何区别。但当heap堆内存空间不足时,系统也可以回收软引用引用的Person对象,从而提高程序运行性能,避免垃圾回收。当程序使用强引用时,无论系统堆内存如何紧张,JVM垃圾回收机制都不会回收被强引用所引用的Java对象,因此最后导致程序因内存不足而中止。但如果把强引用改为软引用,就完成可以避免这种情况,这就是软引用的优势所在。
(3)弱引用  弱引用与软引用有点相似,区别在于弱引用所引用对象的生存期更短。弱引用通过WeakReference类实现,弱引用和软引用很像,但弱引用的引用级别更低。对于只有弱引用的对象而言,当系统垃圾回收机制运行时,不管系统内存是否足够,总会回收该对象所占用的内存。当然,并不是说当一个对象只有弱引用时,它就会立即被回收,正如那些失去引用的对象一样,必须等到系统垃圾回收机制运行时才会被回收.总结说明:1.弱引用具有很大的不确定性,因为每次垃圾回收机制执行时都会回收弱引用所引用的对象,而垃圾回收机制的运行又不受程序员的控制,因此程序获取弱引用所引用的java对象时必须小心空指针异常,通过弱引用所获取的java对象可能是null2.由于垃圾回收的不确定性,当程序希望从弱引用中取出被引用对象时,可能这个被引用对象已经被释放了。如果程序需要使用被引用的对象,则必须重新创建该对象。
(4)虚引用  软引用和弱引用可以单独使用,但虚引用不能单独使用,单独使用虚引用没有太大的意义。虚引用的主要作用就是跟踪对象被垃圾回收的状态,程序可以通过检查虚引用关联的引用队列中是否包含指定的虚引用,从而了解虚引用所引用的对象是否将被回收.  引用队列由java.lang.ref.ReferenceQueue类表示,它用于保存被回收对象的引用。当把软引用,弱引用和引用队列联合使用时,系统回收被引用的对象之后,将会把被回收对象对应的引用添加到关联的引用队列中。与软引用和弱引用不同的是,虚引用在对象被释放之前,将把它对应的虚引用添加到关联的队列中,这使得可以在对象被回收之前采取行动。虚引用通过PhantomReference类实现,它完全类似于没有引用。虚引用对对象本身没有大的影响,对象甚至感觉不到虚引用的存在。如果一个对象只有一个虚引用,那它和没有引用的效果大致相同。虚引用主要用于跟踪对象被垃圾回收的状态,虚引用不能单独使用,虚引用必须和队列ReferenceQueue联合使用.
二、Java内存泄露
  对于c++程序来说,对象占用的内存空间都必须由程序显式回收,如果程序员忘记了回收它们,那它们所占用的内存空间就会产生内存泄漏;对于java程序来说,所有不可达的对象都由垃圾回收机制负责回收,因此程序员不需要考虑这部分的内存泄漏。但如果程序中有一些java对象,它们处于可达状态,但程序以后永远都不会再访问它们,那它们所占用的空间也不会被回收,它们所占用的空间也会产生内存泄漏。
三、内存管理的小技巧
  尽可能多的掌握Java的内存回收,垃圾回收机制是为了更好地管理JVM的内存,这样才能提高java程序的运行性能。根据前面介绍的内存机制,下面给出java内存管理的几个小技巧。
(1)尽量使用直接量  当需要使用字符串,还有Byte,Short,Integer,Long,Float,Double,Boolean,Charater包装类的实例时,程序不应该采用new的方式来创建对象,而应该直接采用直接量来创建它们。例如,程序需要"hello"字符串,应该采用如下代码String str="hello"'上面这种方式创建一个"hello"字符串,而且JVM的字符串缓存池还会缓存这个字符串。但如果程序采用String str=new String("hello");
  此时程序同样创建了一个缓存在字符串缓存池中的"hello"字符串。除此之外,str所引用的String对象底层还包含一个char[]数组,这个char[]数组里依次存放了h,e,l,l.o等字符串。
(2)使用StringBuffer和StringBuilder进行字符串拼接  如果程序中采用多个String对象进行字符串连接运算,在运行时将生成大量临时字符串,这些字符串会保存在内存中从而导致程序性能下降
(3)尽早释放无用对象的引用  大部分时候,方法局部引用变量所引用的对象会随着方法结束而变成垃圾,因为局部变量的生存周期很短,当方法运行结束之时,该方法内的局部变量就结束了生命周期。因此,大部分时候程序无需将局部引用变量显式设为null.但是下面程序中的情形则需显式设为null比较好了如public void info() {
Object obj=new Objec();
System.out.println(obj.toString());
System.out.println(obj.toString());
//执行耗时,耗内存的操作
//或者调用耗时,耗内存的操作的方法
  对于上面程序所示的info()方法,如果需要“执行耗时,耗内存的操作”或者"或者调用耗时,耗内存的操作的方法",那么上面程序中显式设置obj=null就是有必要的了。可能的情况是:当程序在“执行耗时,耗内存的操作”或者"或者调用耗时,耗内存的操作的方法",obj之前所引用的对象可能被垃圾加收了。
(4)尽量少用静态变量  从理论来说,Java对象对象何时被回收由垃圾回收机制决定,对程序员来说是不确定的。由于垃圾回收机制判断一个对象是否是垃圾的唯一标准就是该对象是否有引用变量引用它,因此要尽早释放对象的引用。  最坏的情况是某个对象被static变量所引用,那么垃圾回收机制通常是不会回收这个对象所占用的内存的。如Class Person {
static Object obj=new Object();
  对于上面的Object对象而言,只要obj变量还引用它,就会不会被垃圾回收机制所回收Person类所对应的Class对象会常驻内存,直到程序结束,因此obj所引用的Object对象一旦被创建,也会常驻内存,直到程序运行结束。
(5)避免在经常调用的方法,循环中创建Java对象如public class Test {
public static void main(String[] args) {
for(int i=0;i&10;i++) {
Object obj=new Object();
//执行其它操作...
  上面物循环产生了10个对象,系统要不断地为这些对象分配内存空间,执行初始化操作。它们的生存时间并不长,接下来系统又需要回收它们所占用的内存空间是,这种不断分配内存,回收操作中,程序的性能受到了很大的影响。
(6)缓存经常使用的对象  如果有些对象需要经常使用,可以考虑把这些对象用缓存池保存起来,这样下次需要时就可直接拿出来这些对象来用。典型的缓存池是数据连接池,数据连接池里缓存了大量的数据库连接,每次程序需要访问数据库时都可直接取出数据库连接。  除此之外,如果系统里还有一些常用的基础信息,比如信息化信息里包含的员工信息,物料信息等,也可以考虑对它们进行缓存。使用缓存通常有两种方法1.使用HashMap进行缓存2.直接使用开源缓存项目。(如OSCache,Ehcahe等)
(7)尽量不要用finalize方法  在一个对象失去引用之后,垃圾回收器准备回收该对象之前,垃圾回收器会先调用对象的finalize()方法来执行资源清理。出于这种考虑,可能有些开发者会考虑使用finalize()方法来进和清理。在垃圾回收器本身已经严重制约应用程序性能的情况下,如果再选择使用finalize方法进行资源清理,无疑是一种火上浇油的行为,这将导致垃圾回收器的负担更大,导致程序运行效率更低
(8)考虑使用SoftReference软引用
3.Java内存管理--内存分配
一、Java内存分配
1、 Java有几种存储区域?
寄存器-- 在CPU内部,开发人员不能通过代码来控制寄存器的分配,由编译器来管理
栈-- 在Windows下, 栈是向低地址扩展的数据结构,是一块连续的内存的区域,即栈顶的地址和栈的最大容量是系统预先规定好的。-- 优点:由系统自动分配,速度较快。-- 缺点:不够灵活,但程序员是无法控制的。-- 存放基本数据类型、开发过程中就创建的对象(而不是运行过程中)
堆-- 是向高地址扩展的数据结构,是不连续的内存区域-- 在堆中,没有堆栈指针,为此也就无法直接从处理器那边获得支持-- 堆的好处是有很大的灵活性。如Java编译器不需要知道从堆里需要分配多少存储区域,也不必知道存储的数据在堆里会存活多长时间。
静态存储区域与常量存储区域-- 静态存储区用来存放static类型的变量-- 常量存储区用来存放常量类型(final)类型的值,一般在只读存储器中
非RAM存储-- 如流对象,是要发送到另外一台机器上的-- 持久化的对象,存放在磁盘上
2、 java内存分配
基础数据类型直接在栈空间分配;
方法的形式参数,直接在栈空间分配,当方法调用完成后从栈空间回收;
引用数据类型,需要用new来创建,既在栈空间分配一个地址空间,又在堆空间分配对象的类变量;
方法的引用参数,在栈空间分配一个地址空间,并指向堆空间的对象区,当方法调用完后从栈空间回收;
局部变量 new 出来时,在栈空间和堆空间中分配空间,当局部变量生命周期结束后,栈空间立刻被回收,堆空间区域等待GC回收;
方法调用时传入的 literal 参数,先在栈空间分配,在方法调用完成后从栈空间释放;
字符串常量在 DATA 区域分配 ,this 在堆空间分配;
数组既在栈空间分配数组名称, 又在堆空间分配数组实际的大小!
3、Java内存模型
Java虚拟机将其管辖的内存大致分三个逻辑部分:方法区(Method Area)、Java栈和Java堆。
方法区是静态分配的,编译器将变量在绑定在某个存储位置上,而且这些绑定不会在运行时改变。常数池,源代码中的命名常量、String常量和static 变量保存在方法区。
Java Stack是一个逻辑概念,特点是后进先出。一个栈的空间可能是连续的,也可能是不连续的。最典型的Stack应用是方法的调用,Java虚拟机每调用一次方法就创建一个方法帧(frame),退出该方法则对应的 方法帧被弹出(pop)。栈中存储的数据也是运行时确定的?
Java堆分配(heap allocation)意味着以随意的顺序,在运行时进行存储空间分配和收回的内存管理模型。
堆中存储的数据常常是大小、数量和生命期在编译时无法确定的。Java对象的内存总是在heap中分配。
4、Java内存分配实例解析
常量池(constant pool)指的是在编译期被确定,并被保存在已编译的.class文件中的一些数据。它包括了关于类、方法、接口等中的常量,也包括字符串常量。常量池在运行期被JVM装载,并且可以扩充。String的intern()方法就是扩充常量池的一个方法;当一个String实例str调用intern()方法时,Java查找常量池中是否有相同Unicode的字符串常量,如果有,则返回其引用,如果没有,则在常量池中增加一个Unicode等于str的字符串并返回它的引用。例:
String s1=new String("kvill");
String s2=s1.intern();
System.out.println( s1==s1.intern() );//false
System.out.println( s1+" "+s2 );// kvill kvill
System.out.println( s2==s1.intern() );//true
这个类中事先没有声名”kvill”常量,所以常量池中一开始是没有”kvill”的,当调用s1.intern()后就在常量池中新添加了一个 ”kvill”常量,原来的不在常量池中的”kvill”仍然存在。s1==s1.intern()为false说明原来的“kvill”仍然存在;s2 现在为常量池中“kvill”的地址,所以有s2==s1.intern()为true。
String 常量池问题
(1) 字符串常量的"+"号连接,在编译期字符串常量的值就确定下来, 拿"a" + 1来说,编译器优化后在class中就已经是a1。
String a = "a1";
String b = "a" + 1;
System.out.println((a == b)); //result = true
String a = "atrue";
String b = "a" + "true";
System.out.println((a == b)); //result = true
String a = "a3.4";
String b = "a" + 3.4;
System.out.println((a == b)); //result = true
(2) 对于含有字符串引用的"+"连接,无法被编译器优化。
String a = "ab";
String bb = "b";
String b = "a" +
System.out.println((a == b)); //result = false
由于引用的值在程序编译期是无法确定的,即"a" + bb,只有在运行期来动态分配并将连接后的新地址赋给b。
(3) 对于final修饰的变量,它在编译时被解析为常量值的一个本地拷贝并存储到自己的常量池中或嵌入到它的字节码流中。所以此时的"a" + bb和"a" + "b"效果是一样的。
String a = "ab";
final String bb = "b";
String b = "a" +
System.out.println((a == b)); //result = true
(4) jvm对于字符串引用bb,它的值在编译期无法确定,只有在程序运行期调用方法后,将方法的返回值和"a"来动态连接并分配地址为b。
String a = "ab";
final String bb = getbb();
String b = "a" +
System.out.println((a == b)); //result = false
private static string getbb() {
return "b";
(5) String 变量采用连接运算符(+)效率低下。
String s = "a" + "b" + "c"; 就等价于String s = "abc";
String a = "a";
String b = "b";
String c = "c";
String s = a + b +
这个就不一样了,最终结果等于:
Stringbuffer temp = new Stringbuffer();
temp.append(a).append(b).append(c);
String s = temp.toString();
(6) Integer、Double等包装类和String有着同样的特性:不变类。
String str = "abc"的内部工作机制很有代表性,以Boolean为例,说明同样的问题。不变类的属性一般定义为final,一旦构造完毕就不能再改变了。Boolean对象只有有限的两种状态:true和false,将这两个Boolean对象定义为命名常量:
public static final Boolean TRUE = new Boolean(true);
public static final Boolean FALSE = new Boolean(false);
这两个命名常量和字符串常量一样,在常数池中分配空间。 Boolean.TRUE是一个引用,Boolean.FALSE是一个引用,而"abc"也是一个引用!由于Boolean.TRUE是类变量(static)将静态地分配内存,所以需要很多Boolean对象时,并不需要用new表达式创建各个实例,完全可以共享这两个静态变量。其JDK中源代码是:
public static Boolean valueOf(boolean b) {
return (b ? TRUE : FALSE);
基本数据(Primitive)类型的自动装箱(autoboxing)、拆箱(unboxing)是JSE 5.0提供的新功能。 Boolean b1 = 5&3; 等价于Boolean b1 = Boolean.valueOf(5&3); //优于Boolean b1 = new Boolean (5&3);
static void foo(){
boolean isTrue = 5&3; //基本类型
Boolean b1 = Boolean.TRUE; //静态变量创建的对象
Boolean b2 = Boolean.valueOf(isTrue);//静态工厂
Boolean b3 = 5&3;//自动装箱(autoboxing)
System.out.println("b1 == b2 ?" +(b1 == b2));
System.out.println("b1 == b3 ?" +(b1 == b3));
Boolean b4 = new Boolean(isTrue);////不宜使用
System.out.println("b1 == b4 ?" +(b1 == b4));//浪费内存、有创建实例的时间开销
} //这里b1、b2、b3指向同一个Boolean对象。
(7) 如果问你:String x ="abc";创建了几个对象?
准确的答案是:0或者1个。如果存在"abc",则变量x持有"abc"这个引用,而不创建任何对象。如果问你:String str1 = new String("abc"); 创建了几个对象?准确的答案是:1或者2个。(至少1个在heap中)
(8) 对于int a = 3; int b = 3;
编译器先处理int a = 3;首先它会在栈中创建一个变量为a的引用,然后查找有没有字面值为3的地址,没找到,就开辟一个存放3这个字面值的地址,然后将a指向3的地址。接着处理int b = 3;在创建完b的引用变量后,由于在栈中已经有3这个字面值,便将b直接指向3的地址。这样,就出现了a与b同时均指向3的情况。5、堆(Heap)和非堆(Non-heap)内存按照官方的说法:“Java 虚拟机具有一个堆,堆是运行时数据区域,所有类实例和数组的内存均从此处分配。堆是在 Java 虚拟机启动时创建的。”可以看出JVM主要管理两种类型的内存:堆和非堆。简单来说堆就是Java代码可及的内存,是留给开发人员使用的;非堆就是JVM留给自己用的,所以方法区、JVM内部处理或优化所需的内存(如JIT编译后的代码缓存)、每个类结构(如运行时常数池、字段和方法数据)以及方法和构造方法的代码都在非堆内存中。堆内存分配JVM初始分配的内存由-Xms指定,默认是物理内存的1/64;JVM最大分配的内存由-Xmx指定,默认是物理内存的1/4。默认空余堆内存小于40%时,JVM就会增大堆直到-Xmx的最大限制;空余堆内存大于70%时,JVM会减少堆直到-Xms的最小限制。因此服务器一般设置-Xms、-Xmx相等以避免在每次GC 后调整堆的大小。非堆内存分配JVM使用-XX:PermSize设置非堆内存初始值,默认是物理内存的1/64;由XX:MaxPermSize设置最大非堆内存的大小,默认是物理内存的1/4。例子-Xms256m-Xmx1024m-XX:PermSize=128M-XX:MaxPermSize=256M
4.java内存机制--堆与栈
问题的引入:问题一:
String str1 = "abc";
String str2 = "abc";
System.out.println(str1==str2); //true
String str1 =new String ("abc");
String str2 =new String ("abc");
System.out.println(str1==str2); // false
String s1 = "ja";
String s2 = "va";
String s3 = "java";
String s4 = s1 + s2;
System.out.println(s3 == s4);//false
System.out.println(s3.equals(s4));//true
一、Java 中的堆和栈
Java把内存划分成两种:一种是栈内存,一种是堆内存。在函数中定义的一些基本类型的变量和对象的引用变量都在函数的栈内存中分配。
当在一段代码块定义一个变量时,Java就在栈中为这个变量分配内存空间,当超过变量的作用域后,Java会自动释放掉为该变量所分配的内存空间,该内存空间可以立即被另作他用。
堆内存用来存放由new创建的对象和数组。
在堆中分配的内存,由Java虚拟机的自动垃圾回收器来管理。
在堆中产生了一个数组或对象后,还可以在栈中定义一个特殊的变量,让栈中这个变量的取值等于数组或对象在堆内存中的首地址,栈中的这个变量就成了数组或对象的引用变量。
引用变量就相当于是为数组或对象起的一个名称,以后就可以在程序中使用栈中的引用变量来访问堆中的数组或对象。
具体的说:栈与堆都是Java用来在Ram中存放数据的地方。与C++不同,Java自动管理栈和堆,程序员不能直接地设置栈或堆。Java的堆是一个运行时数据区,类的(对象从中分配空间。这些对象通过new、newarray、anewarray和multianewarray等指令建立,它们不需要程序代码来显式的释放。堆是由垃圾回收来负责的,堆的优势是可以动态地分配内存大小,生存期也不必事先告诉编译器,因为它是在运行时动态分配内存的,Java的垃圾收集器会自动收走这些不再使用的数据。但缺点是,由于要在运行时动态分配内存,存取速度较慢。栈的优势是,存取速度比堆要快,仅次于寄存器,栈数据可以共享。但缺点是,存在栈中的数据大小与生存期必须是确定的,缺乏灵活性。栈中主要存放一些基本类型的变量(int, short, long, byte, float, double, boolean, char)和对象句柄。栈有一个很重要的特殊性,就是存在栈中的数据可以共享。假设我们同时定义:
int a = 3;
int b = 3;
编译器先处理int a = 3;首先它会在栈中创建一个变量为a的引用,然后查找栈中是否有3这个值,如果没找到,就将3存放进来,然后将a指向3。接着处理int b = 3;在创建完b的引用变量后,因为在栈中已经有3这个值,便将b直接指向3。这样,就出现了a与b同时均指向3的情况。这时,如果再令a=4;那么编译器会重新搜索栈中是否有4值,如果没有,则将4存放进来,并令a指向4;如果已经有了,则直接将a指向这个地址。因此a值的改变不会影响到b的值。要注意这种数据的共享与两个对象的引用同时指向一个对象的这种共享是不同的,因为这种情况a的修改并不会影响到b, 它是由编译器完成的,它有利于节省空间。而一个对象引用变量修改了这个对象的内部状态,会影响到另一个对象引用变量。
String是一个特殊的包装类数据。可以用:
String str = new String("abc");
String str = "abc";
两种的形式来创建,第一种是用new()来新建对象的,它会在存放于堆中。每调用一次就会创建一个新的对象。而第二种是先在栈中创建一个对String类的对象引用变量str,然后查找栈中有没有存放"abc",如果没有,则将"abc"存放进栈,并令str指向”abc”,如果已经有”abc” 则直接令str指向“abc”。
比较类里面的数值是否相等时,用equals()方法;当测试两个包装类的引用是否指向同一个对象时,用==,下面用例子说明上面的理论。
String str1 = "abc";
String str2 = "abc";
System.out.println(str1==str2); //true
可以看出str1和str2是指向同一个对象的。
String str1 =new String ("abc");
String str2 =new String ("abc");
System.out.println(str1==str2); // false
用new的方式是生成不同的对象。每一次生成一个。因此用第二种方式创建多个”abc”字符串,在内存中其实只存在一个对象而已. 这种写法有利与节省内存空间. 同时它可以在一定程度上提高程序的运行速度,因为JVM会自动根据栈中数据的实际情况来决定是否有必要创建新对象。而对于String str = new String("abc");的代码,则一概在堆中创建新对象,而不管其字符串值是否相等,是否有必要创建新对象,从而加重了程序的负担。另一方面, 要注意: 我们在使用诸如String str = "abc";的格式定义类时,总是想当然地认为,创建了String类的对象str。担心陷阱!对象可能并没有被创建!而可能只是指向一个先前已经创建的对象。只有通过new()方法才能保证每次都创建一个新的对象。由于String类的immutable性质,当String变量需要经常变换其值时,应该考虑使用StringBuffer类,以提高程序效率。
二、java中内存分配策略及堆和栈的比较
2.1 内存分配策略
  按照编译原理的观点,程序运行时的内存分配有三种策略,分别是静态的,栈式的,和堆式的。  静态存储分配是指在编译时就能确定每个数据目标在运行时刻的存储空间需求,因而在编译时就可以给他们分配固定的内存空间.这种分配策略要求程序代码中不允许有可变数据结构(比如可变数组)的存在,也不允许有嵌套或者递归的结构出现,因为它们都会导致编译程序无法计算准确的存储空间需求.  栈式存储分配也可称为动态存储分配,是由一个类似于堆栈的运行栈来实现的.和静态存储分配相反,在栈式存储方案中,程序对数据区的需求在编译时是完全未知的,只有到运行的时候才能够知道,但是规定在运行中进入一个程序模块时,必须知道该程序模块所需的数据区大小才能够为其分配内存.和我们在数据结构所熟知的栈一样,栈式存储分配按照先进后出的原则进行分配。  静态存储分配要求在编译时能知道所有变量的存储要求,栈式存储分配要求在过程的入口处必须知道所有的存储要求,而堆式存储分配则专门负责在编译时或运行时模块入口处都无法确定存储要求的数据结构的内存分配,比如可变长度串和对象实例.堆由大片的可利用块或空闲块组成,堆中的内存可以按照任意顺序分配和释放.
2.2 堆和栈的比较
  上面的定义从编译原理的教材中总结而来,除静态存储分配之外,都显得很呆板和难以理解,下面撇开静态存储分配,集中比较堆和栈:  从堆和栈的功能和作用来通俗的比较,堆主要用来存放对象的,栈主要是用来执行程序的.而这种不同又主要是由于堆和栈的特点决定的:在编程中,例如C/C++中,所有的方法调用都是通过栈来进行的,所有的局部变量,形式参数都是从栈中分配内存空间的。实际上也不是什么分配,只是从栈顶向上用就行,就好像工厂中的传送带(conveyor belt)一样,Stack Pointer会自动指引你到放东西的位置,你所要做的只是把东西放下来就行.退出函数的时候,修改栈指针就可以把栈中的内容销毁.这样的模式速度最快, 当然要用来运行程序了.需要注意的是,在分配的时候,比如为一个即将要调用的程序模块分配数据区时,应事先知道这个数据区的大小,也就说是虽然分配是在程序运行时进行的,但是分配的大小多少是确定的,不变的,而这个"大小多少"是在编译时确定的,不是在运行时.  堆是应用程序在运行的时候请求操作系统分配给自己内存,由于从操作系统管理的内存分配,所以在分配和销毁时都要占用时间,因此用堆的效率非常低.但是堆的优点在于,编译器不必知道要从堆里分配多少存储空间,也不必知道存储的数据要在堆里停留多长的时间,因此,用堆保存数据时会得到更大的灵活性。事实上,面向对象的多态性,堆内存分配是必不可少的,因为多态变量所需的存储空间只有在运行时创建了对象之后才能确定.在C++中,要求创建一个对象时,只需用 new命令编制相关的代码即可。执行这些代码时,会在堆里自动进行数据的保存.当然,为达到这种灵活性,必然会付出一定的代价:在堆里分配存储空间时会花掉更长的时间!这也正是导致我们刚才所说的效率低的原因,看来列宁同志说的好,人的优点往往也是人的缺点,人的缺点往往也是人的优点.
2.3 JVM中的堆和栈
  JVM是基于堆栈的虚拟机.JVM为每个新创建的线程都分配一个堆栈.也就是说,对于一个Java程序来说,它的运行就是通过对堆栈的操作来完成的。堆栈以帧为单位保存线程的状态。JVM对堆栈只进行两种操作:以帧为单位的压栈和出栈操作。  我们知道,某个线程正在执行的方法称为此线程的当前方法.我们可能不知道,当前方法使用的帧称为当前帧。当线程激活一个Java方法,JVM就会在线程的 Java堆栈里新压入一个帧。这个帧自然成为了当前帧.在此方法执行期间,这个帧将用来保存参数,局部变量,中间计算过程和其他数据.这个帧在这里和编译原理中的活动纪录的概念是差不多的.  从Java的这种分配机制来看,堆栈又可以这样理解:堆栈(Stack)是操作系统在建立某个进程时或者线程(在支持多线程的操作系统中是线程)为这个线程建立的存储区域,该区域具有先进后出的特性。  每一个Java应用都唯一对应一个JVM实例,每一个实例唯一对应一个堆。应用程序在运行中所创建的所有类实例或数组都放在这个堆中,并由应用所有的线程共享.跟C/C++不同,Java中分配堆内存是自动初始化的。Java中所有对象的存储空间都是在堆中分配的,但是这个对象的引用却是在堆栈中分配,也就是说在建立一个对象时从两个地方都分配内存,在堆中分配的内存实际建立这个对象,而在堆栈中分配的内存只是一个指向这个堆对象的指针(引用)而已。
只要坚持21天,这就是你接下来的习惯。

我要回帖

更多关于 痛苦练舞蹈基本功视频 的文章

 

随机推荐