到内存一个java字符串数组排序数组里,再解析是否能提高效率

Java里的字符串, String类简单介绍.
我的图书馆
Java里的字符串, String类简单介绍.
String类在java面试中也是1个常见的问题点. 所以也是写在这里方便以后查阅了.
大家都知道c语言里是没有String 字符串这个数据类型的.
只能用字符数组的1个特殊形式来表示一个字符串, 就是这个字符数组的最后1元素必须是以'\0'(空) 来结尾的.
char c[] = "abcd" 是1个字符串, 但是它的长度是5, char[4] = '\0'
char d[6] = "abcde" 也是1个字符串.
但是, char e[5] = "abcde" 只是1个字符数组, 因为它最后1个元素不是 = "\0" ,如果对字符数组e执行string.h里面的函数的话, 就肯定会出错了.
因为检测不到'\0'字符啊.
当然, 用c语言写1个字符串容器不难.& 而java是由c语言发展而来, sun公司已经帮我们写好了1个字符串容器, 这个容器就是String类了.
一, Java里的字符串.
1.1 字符串跟String类是不同的概念
本文涉及两个重点,& 1个是字符串, 1个是String类. 它们虽然有联系, 但是却是完全不同的两个概念!
我们可以参考jdk api中文里对String类的解释:
public final class String
extends Object
implements Serializable, Comparable&String&, CharSequence
String 类代表字符串。Java 程序中的所有字符串字面值(如 "abc" )都作为此类的实例实现。
字符串是常量;它们的值在创建之后不能更改。字符串缓冲区支持可变的字符串。因为 String 对象是不可变的,所以可以共享。
由上面的解析我们见到几个不易理解的地方:
字符串是常量?
它们的值不能改?
因为..所以..& 这什么逻辑?
在实际编程当中, 我们觉得字符串变量的值可以更改的呀?
本人认为, 大家没必要担心自己的理解能力, 中文jdk api的翻译实在是很没有节操的.
上面解释的最后一句话的原文是这样的:
Strings their values cannot be changed after they are created.String buffers support mutablestrings.
Because String objects are immutable they can be shared
字符串是常量; 它们的值一旦创建不能更改. 然而String类的引用(变量 or 内存)却可以指向不同的字符串. 是因为字符串对象虽然是不能修改的,
但是它们的地址可以共享.
原文和本人翻译都有两种颜色的单词,& 红色就是指上面第一个概念字符串.& 而蓝色指的另1个概念String类.
相信即使本人修改了翻译, 仍然会有人觉得还是不能理解, 请往下看:
1.2 java里字符串的定义
注意这个不是String类的定义哦, 定义如下:
Java里的字符串就是存放于数据区(静态区)以Unicode编码的字符集合.
可见java里的字符串跟c语言的字符串如下两个本质上的区别:
1.2.1 Java里字符串用Unicode编码
c语言中的字符串里面的字符串是用ASCII编码的, ASCII只用1个字节(byte)的内存表示1个字符. 但是1个字节的内存数量不足以表示全世界那么多种字符.
例如1个汉子就需要2个字节来表示.
所以c语言里的某些字符处理函数, 如果参数传入1个汉字可能就会出错, 因为毕竟字符的占用内存长度不同.
而Unicdoe也叫万国码, 它用两个字节去表示任何1个字节, 无论是字母还是汉字. 所以利用java来做内存处理更加方便, 跨平台性非常好.
缺点就是比c语言字符处理更加耗内存.
1.2.2 Java里字符串存放在数据区(静态区).
我之前的博文见过, java程序类似于c语言, 运行时会把程序占用的内存大致分割成几个部分.
stuck(栈区), Heap(堆区), Data(数据区)和代码区&
其中数据区用于存放静态变量和字符串常量.
一, Java里的字符串.
1.3 为什么说java里的字符串是常量, 不可修改的.
1.3.1 一般的类指向的是变量
关于这点, 需要对比才能讲得清楚.
这里我们利用1个自定的类来举个例子:
[java] package&String_&&&&class&Human_1{&&&&&&int&&&&&&&int&&&&&&&public&Human_1(int&id,&int&age){&&&&&&&&&&this.id&=&&&&&&&&&&&this.age&=&&&&&&&}&&&&&&public&String&toString(){&&&&&&&&&&return&"id&is&"&+&id&+&","&&+&"&age&is&"&+&&&&&&&}&&}&&&&public&class&String_2{&&&&&&public&static&void&f(){&&&&&&&&&&Human_1&h&=&new&Human_1(1,30);&&&&&&&&&&Human_1&h2&=&h;&//&&&&&&&&&&System.out.printf("h:&%s\n",&h.toString());&&&&&&&&&&&System.out.printf("h2:&%s\n\n",&h.toString());&&&&&&&&&&&&&h.id&=&3;&&&&&&&&&&h.age&=&32;&&&&&&&&&&System.out.printf("h:&%s\n",&h.toString());&&&&&&&&&&&System.out.printf("h2:&%s\n\n",&h.toString());&&&&&&&&&&&&&System.out.println(&h&==&h2&);&&&&&&}&&}&&
上面例子中定义了1个Human_1的类, 只有2个成员id和age.
下面f()中首先实例化了1个Human_1的对象h.
然后定义另外1个引用h2, 然后把h的地址赋予给h2 ( Human_1 h2 = h)
然后输出h, h2的值, 它们是一样的.
然后修改h的值,
再次输出h h2的值, 发现h2的值也被修改.
最后用 " == " 来比较h 和 h2所指向的地址.
明显它们两者所指向的地址是相同.
[java] [java]&h:&id&is&1,&age&is&30&&[java]&h2:&id&is&1,&age&is&30&&[java]&&&[java]&h:&id&is&3,&age&is&32&&[java]&h2:&id&is&3,&age&is&32&&[java]&&&[java]&true&&
其实上面例子可以理解成:
首先用1个容器h2 来保存 h1所指向的地址.
然后修改h1的值, 最后h1的地址没变化.
h这个对象虽然值被修改了, 但是指向的内存地址没有变化, 变的是该内存的内容(值)
画张图便于理解:
如上图可见:
1& 对象名h 和 h2 本身是都是局部变量, 位于栈区中, 里面存放的是1个地址.
2. 该地址指向的是堆区的一块内存, 这块内存是用new Human()划分出来的. 而且把头部地址赋予对象名h
3. 该内存包括两个部分, 1个用于存放成员id的值, 另1个存放成员age的值.
可见, 无论对象h成员的值如何改变, 变的只是堆区内存的存放内容, 而堆区内存地址是没变化的. 对象引用h的指向也没有变化.
我们一般把这种内存地址不变, 值可以改变的东西成为变量.
意思就是内存地址不变的前提下内存的内容是可变的.
注意, 上面的例子不说是对象引用h是1个变量,& 而是说h指向的内存是1个变量
1.3.2 java里的字符串是常量
将上面的例子改一下, 把Human_1类改成String类:
[java] package&String_&&&&public&class&String_3{&&&&&&public&static&void&f(){&&&&&&&&&&String&s&=&"cat";&&&&&&&&&&String&s2&=&s;&&&&&&&&&&&&System.out.printf("s:&%s\n",&s);&&&&&&&&&&&System.out.printf("s2:&%s\n",&s2);&&&&&&&&&&&System.out.println(s&==&s2);&&&&&&&&&&&&&s&=&"dog";&&&&&&&&&&System.out.printf("\ns:&%s\n",&s);&&&&&&&&&&&System.out.printf("s2:&%s\n",&s2);&&&&&&&&&&&System.out.println(s&==&s2);&&&&&&&}&&}&&
逻辑跟上面的例子基本没区别, 也是首先实例化1个String对象s, 它的值是s;
然后将s所指向的地址保存在另个引用s2.
这时输出s 和 s2的值, 它们当然是相等的.
这时"修改"s的值为"dog"
再输出s 和 s2的值, 却发现s的值变成dog了, 但是s2的值还是cat..& 而且它们的所指向的地址也不再相等.
[java] [java]&s:&cat&&[java]&s2:&cat&&[java]&true&&[java]&&&[java]&s:&dog&&[java]&s2:&cat&&[java]&false&&
为什么s 和 s2所指向的地址一开始是相等的, 一旦s的修改为dog后, s 和 s2所指向的地址就不等呢.
原因就是这句代码:
s = "dog";&
并不是修改s所指向的内存地址, 而是改变了s的指向, 也就是修改了s的所指向的地址啊.
s的值"修改"前:
由上图可见:
1. String类也是java的类, 所以它的实例化对象也需要在堆区划分内存。
2. 两个对象引用s 和 s2这时都指向了堆区同1块对象内存。所以它们的所指向地址是相等的。
3. 字符串真正的地址不是再堆区中, 是在数据区中的。 而堆区对象内存中有其中1个对象成员保存了该字符串在数据区的真正地址。
s的值"修改"为dog后:
由上图可见:
1. s = "dog" 并不是修改s所指向的内容. 而是在堆区和数据区各划分了1个新的内存. 其中数据区划分1个新的字符串"dog" , 堆区划分1个新的String对象内存, 保存了dog的字符串地址.
2. 当然之前那个堆区对象内存和数据"cat"的内存是由 String s = "cat" 这条语句创建,关于String类语法机制后面会再讲。
3. s = "dog" 不但在数据区和堆区都各自创建1个新内存, 而且还改变了自己所指向的地址, 所以这时s 和 s2 不再相等.
4. 关键是原来数据的字符"cat" 并没有被修改! 也不可能被修改.
我们一般把这种内存值不能改变, 只能通过引用去指向另1块的东西叫做常量.
1.3.3 java里字符串不能修改的一些小结.
其实从另外一些方面一也能体现出java字符串不能修改的.
例如一些java的内部类, 如Calendar(日期)一般都会提供一些setXXXX的方法让程序猿去修改对应的值. 例如 setYear(), setDate().等等
而String类是没有这类setXXXX方法.
虽然字符串在数据区中的内存不能修改, 但是我们可以为String类的对象指向另一块内存. 所以这个特性在编程造成的影响不大.
那么原来的内存怎么办呢? 放心, java的内存回收机制会收拾它们的..
二, Java里的String类.
2.1 java里 String 类的 本质
String类的书面解释在本文的1.1 章里提高过了, 是1个用于字符串的类.
但是这个解释并没有指明String类的本质.
我们知道, Java类的本质大致上可以理解为 成员(属性) 和 方法的集合体.
String类也一样, 只不过String类有1个关键的成员, 这个成员保存着数据区的某个字符串的内存地址. 可以理解为1个指针.
而String类的方法是一些对对应字符串的查询方法(例如indexOf(), charAt()等). 注意, 并没有对这个字符串进行修改的方法哦, 字符串是常量, 不能修改.
虽然String类不能修改字符串, 但是上面保存字符串地址的成员却是可以被改变的, 也就是说String类的对象可以指向另1个字符串.
见上图, java的String类实例化1个对象后, 会在堆区划分一块对象的内存, 其中1个关键成员存放的是数据区字符串的地址.
而下面若干个方法内存, 存放的是该函数(方法)在代码区的2进制代码的地址.
2.2 String类实例化对象的第一个方法. new String("abc")
当然, String类的构造函数有很多个(参数不同), 但是在coding中,常用的实例化对象方法无非是两种.
第一种就是与其他类一样, 利用构造方法.
[java] String&s&=&new&String("abc");&&
上面的代码做了下面若干个事情.
1. 在数据区中划分一块内存存放字符串, 值是"abc", 这块内存一旦创建, 值"abc" 不能被修改.
2. 在堆区划分1块对象内存, 其中小块用于存放上面字符串的地址, 另一些用于存放函数指针.
3. 在栈区划分一块内存, 存放上面堆区的头部地址.
下面是1个例子:
[java] package&String_&&&&public&class&String_4{&&&&&&public&static&void&f(){&&&&&&&&&&String&s&=&new&String("cat");&&&&&&&&&&String&s2&=&new&String("cat");&&&&&&&&&&&&System.out.printf("s:&%s\n",&s);&&&&&&&&&&&System.out.printf("s2:&%s\n",&s2);&&&&&&&&&&&System.out.println(s&==&s2);&&&&&&&&&&&System.out.println(s.equals(s2));&&&&&&}&&}&&
上面利用new 实例化了两个对象s和s2 , 它们所指向的字符串值都是"cat"
然后用 "==" 和 equals来比较两者
[plain] [java]&s:&cat&&[java]&s2:&cat&&[java]&false&&[java]&true&&
可见用equals 来比较s 和 s2, 它们是相等的, 因为它们的内容相同. 而且equals方法在String类里重写过了.
而用 "==" 比较的是两个对象s 和 s2所指向的地址, 它们所指向的地址是不同的.
亦即系讲, 两个new语句分别在数据区和堆区各自都划分2个内存.
数据区中有两个字符串内存, 它们的值是一样的都是"cat".
堆区有两个对象内存, 它们分别保存了各自对应的字符串地址.
而stuck区中两个s1 s2 保存了各自的堆区内存地址.& 这两个地址明显是不同的. 也就是 s == s2 返回false的原因.
2.3 String类实例化对象的另一个方法.& = "abc"
事实上, 我们在编程中新建1个字符串更多情况下会用如下的方式:
[java] String&s&=&"abc";&&
这种方式更上面那种有什么区别呢?
[java] package&String_&&&&public&class&String_5{&&&&&&public&static&String&g(){&&&&&&&&&&String&s4&=&"cat";&&&&&&&&&&return&s4;&&&&&&}&&&&&&&&public&static&void&f(){&&&&&&&&&&String&s&=&new&String("cat");&&&&&&&&&&String&s2&=&"cat";&&&&&&&&&&String&s3&=&"cat";&&&&&&&&&&System.out.printf("s:&%s\n",&s);&&&&&&&&&&&System.out.printf("s2:&%s\n",&s2);&&&&&&&&&&&System.out.printf("s3:&%s\n",&s3);&&&&&&&&&&&&&&&&&&&&&System.out.println(s&==&s2);&&&&&&&&&&&System.out.println(s2&==&s3);&&&&&&&&&&&System.out.println(s2&==&g());&&&&&&&}&&}&&
这个例子步骤也不复杂:
首先f()方法里 利用第一种方法实例化了1个值为"cat"的对象s
然后里利用第二种方法 又 创建了两个String 对象s2 和 s3, 它们的值都是"cat".
然后用"==" 来比较它们.
然后f()方法调用g()方法, g()方法利用第二方式实例化了1个值为"cat"的String 对象s4
最后用 "==" 比较s2 和 s4 的地址.
[plain] [java]&s:&cat&&[java]&s2:&cat&&[java]&s3:&cat&&[java]&false&&[java]&true&&[java]&true&&
由结果得知, s4 和 s2 和 s3的地址是相同的! 而由第一种方法创建的s 跟前面三者地址不同.
所以结论如下:
利用 = "cat" 方式创建1个String对象时, java 首先会检测当前进程的数据区是否有1个以相同方式创建的值是一样的字符串存在.
如果无, 则类似 new Sring("cat")方式, 在数据区和堆区都各自划分一块新内存, 用于该创建的对象.
如果有, 则直接把该对象的地址指向 已存在的堆区内存地址.
也就是讲, 在f() 里的String s2 = "cat" 相当于执行了 String s2 = new String("cat");
而在f()里的 String&& s3 = "cat" 相当执行了String s3 = s2;
而在g()里, 理论上g()是不能访问f()里的 局部变量的, 但是g()还是检测到数据区存在用相同方式创建而且值1个样的字符串.
所以s4 也指向了堆区的那一块内存.
这个例子说明了, 在同1个java程序中, 所有用 " = "abc" " 方式创建的而且具有相同值的多个String对象其实都是同1个对象. 因为它们指向同一块堆区的内存.
由于这种特性, 所以这种用" = "abc"" 方式创建的对象十分适合做 synchronized对象锁 要锁的对象.&& 不用担心锁的是两个不同的对象导致 多线程同步失败.
三, String类的常用方法.
下面是应付面试的, 大家看看就好.
public char charAt(int index) //返回字符串中第index个字符
public int length()& //返回字符串的长度
public int indexOf(String str)
返回字符串中出现str的第1个位置
public int indexOf(String str, int fromIndex)
返回字符串中, 从第fromIndex个字符数起, 出现str的第1个位置, 这个方法是上面方法的重载
public boolean equalsIgnoreCase(String str)
忽略大小写, 比较两个字符是否相等.
public String replace(char oldChar, char newChar)
返回1个新字符串, 该新字符串内的oldChar被newChar替换掉, 注意旧字符串没有被修改.
public boolean startsWith(String prefix)
判断字符串是否以 prefix 开头
public boolean endsWith(String suffix)
判断字符产是否以suffix 结尾
public String subString(int beginIndex)
截取从第beginIndex个字符开始到最后1个字符, 返回1个新字符串
public String subString(int beginIndex, int endIndex)
截取从第beginIndex个字符开始, 第endIndex个字符, 返回1个新字符串, 是上面方法的重载
public static String valueOf(...)
注意这个是静态方法. 可以把其他基本数据类型转换成String对象
Integer.parseInt(String s)
这个是另1个类Integer 的敬爱函数, 可以把字符串转换成int类型.& 会抛出异常..
喜欢该文的人也喜欢C语言解析二进制文件时,先把文件全部读入到内存一个字符数组里,再解析是否能提高效率?-土地公问答
C语言解析二进制文件时,先把文件全部读入到内存一个字符数组里,再解析是否能提高效率?
C语言解析二进制文件时,先把文件全部读入到内存一个字符数组里,再解析是否能提高效率?
之前学校请东软老师给我们实训的时候,出过一个题目:解析一个二进制文件(3M-4M左右)如果采用C语言的文件操作函数解析存在硬盘上的二进制文件别人的做法是:从硬盘读入一组信息,解析一组信息,然后再读入一组信息,如此重复……我的做法是:一次性从硬盘把整个文件读入内存字符数组,然后再解析。理由:读入内存后,内存的响应速度 快过 硬盘,因而提高了效率担忧之处:编译器会对别人的做法进行自动优化,从而达到和自己同样的处理效率。自己岂不是画蛇添足了。。
在内存允许的情况下,我知道的最快的方式是mmap,比fread快多了.. 对于你的疑问,在内存允许的情况下,你的做法确实比别人的快 详见探寻C++最快的读取文件的方案
你可能想复杂了。 如果打算认真学习C语言,推荐必看的《APUE》,中文翻译版本是《Unix环境高级编程》。已经不写C很多年,手边也没有C的编译环境,就不现做测试了。结合《APUE》和自己印象中的以前的测试结果来回答一下。 1.如@Hakunamatata 所言,文件过于…
这里先假定题主的“解析”算法时间复杂度不随着信息分组数增长而增长,那么答案是: 不能 设程序P处理文件F的总耗时是T,按题中所述T=T1+T2,其中T1为读取文件总耗时,T2为解析文件总耗时。 题主的做法:将文件分成多片,简单假设每片大小相等,读取每片耗…
自问自答。五个多月以后的今天,终于弄明白这问题了。 和@AndyHsu 的一样, 如果是用read来解析文件,那么构造一个缓冲区可以提高效率。 如果用fgetc\fscanf等函数,那个构造缓冲区就是画蛇添足了。 下面具体解释一下为什么: 像read\write这些函数都是 操…
其它类似问题
其它人正在问的问题 上传我的文档
 上传文档
 下载
 收藏
粉丝量:12
该文档贡献者很忙,什么也没留下。
 下载此文档
Java数组初始化时内存分配情况解析
下载积分:1500
内容提示:Java数组初始化时内存分配情况解析
文档格式:PDF|
浏览次数:132|
上传日期: 22:50:12|
文档星级:
全文阅读已结束,如果下载本文需要使用
 1500 积分
下载此文档
该用户还上传了这些文档
Java数组初始化时内存分配情况解析
关注微信公众号解析提高PHP执行效率的50个技巧
1、用单引号代替双引号来包含字符串,这样做会更快一些。因为PHP会在双引号包围的字符串中搜寻变量, 单引号则不会,注意:只有echo能这么做,它是一种可以把多个字符串当作参数的”函数”(译注:PHP手册中说echo是语言结构,不是真正的函数,故 把函数加上了双引号)。
2、如果能将类的方法定义成static,就尽量定义成static,它的速度会提升将近4倍。
3、$row[‘id’] 的速度是$row[id]的7倍。
4、echo 比 print 快,并且使用echo的多重参数(译注:指用逗号而不是句点)代替字符串连接,比如echo $str1,$str2。
5、在执行for循环之前确定最大循环数,不要每循环一次都计算最大值,最好运用foreach代替。
6、注销那些不用的变量尤其是大数组,以便释放内存。
7、尽量避免使用__get,__set,__autoload。
8、require_once()代价昂贵。
9、include文件时尽量使用绝对路径,因为它避免了PHP去include_path里查找文件的速度,解析操作系统路径所需的时间会更少。
10、如果你想知道脚本开始执行(译注:即服务器端收到客户端请求)的时刻,使用
$_SERVER[‘REQUEST_TIME’]
11、函数代替正则表达式完成相同功能。
12、str_replace函数比preg_replace函数快,但strtr函数的效率是str_replace函数的四倍。
13、如果一个字符串替换函数,可接受数组或字符作为参数,并且参数长度不太长,那么可以考虑额外写一段替换代码,使得每次传递参数是一个字符,而不是只写一行代码接受数组作为查询和替换的参数。
14、使用选择分支语句(译注:即switch case)好于使用多个if,else if语句。
15、用@屏蔽错误消息的做法非常低效,极其低效。
16、打开apache的mod_deflate模块,可以提高网页的浏览速度。
17、数据库连接当使用完毕时应关掉,不要用长连接。
18、错误消息代价昂贵。
19、在方法中递增局部变量,速度是最快的。几乎与在函数中调用局部变量的速度相当。
20、递增一个全局变量要比递增一个局部变量慢2倍。
21、递增一个对象属性(如:$this-&prop++)要比递增一个局部变量慢3倍。
22、递增一个未预定义的局部变量要比递增一个预定义的局部变量慢9至10倍。
23、仅定义一个局部变量而没在函数中调用它,同样会减慢速度(其程度相当于递增一个局部变量)。PHP大概会检查看是否存在全局变量。
24、方法调用看来与类中定义的方法的数量无关,因为我(在测试方法之前和之后都)添加了10个方法,但性能上没有变化。
25、派生类中的方法运行起来要快于在基类中定义的同样的方法。
26、调用带有一个参数的空函数,其花费的时间相当于执行7至8次的局部变量递增操作。类似的方法调用所花费的时间接近于15次的局部变量递增操作。
27、Apache解析一个PHP脚本的时间要比解析一个静态HTML页面慢2至10倍。尽量多用静态HTML页面,少用脚本。
28、除非脚本可以缓存,否则每次调用时都会重新编译一次。引入一套PHP缓存机制通常可以提升25%至100%的性能,以免除编译开销。
29、尽量做缓存,可使用memcached。memcached是一款高性能的内存对象缓存系统,可用来加速动态Web应用程序,减轻数据库负载。对运算码 (OP code)的缓存很有用,使得脚本不必为每个请求做重新编译。
30、当操作字符串并需要检验其长度是否满足某种要求时,你想当然地会使用strlen()函数。此函数执行起来相当快,因为它不做任何计算, 只返回在zval 结构(C的内置数据结构,用于存储PHP变量)中存储的已知字符串长度。但是,由于strlen()是函数,多多少少会有些慢,因为函数调用会经过诸多步 骤,如字母小写化(译注:指函数名小写化,PHP不区分函数名大小写)、哈希查找,会跟随被调用的函数一起执行。在某些情况下,你可以使用isset() 技巧加速执行你的代码。
(举例如下)
if (strlen($foo) & 5) { echo “Foo is too short”$$ }
(与下面的技巧做比较)
if (!isset($foo{5})) { echo “Foo is too short”$$ }
调用isset()恰巧比strlen()快,因为与后者不同的是,isset()作为一种语言结构,意味着它的执行不需要函数查找和字母小写化。也就是说,实际上在检验字符串长度的顶层代码中你没有花太多开销。
31、当执行变量$i的递增或递减时,$i++会比++$i慢一些。这种差异是PHP特有的,并不适用于其他语言,所以请不要修改你的C或 Java代码并指望它们能立即变快,没用的。++$i更快是因为它只需要3条指令(opcodes),$i++则需要4条指令。后置递增实际上会产生一个 临时变量,这个临时变量随后被递增。而前置递增直接在原值上递增。这是最优化处理的一种,正如Zend的PHP优化器所作的那样。牢记这个优化处理不失为 一个好主意,因为并不是所有的指令优化器都会做同样的优化处理,并且存在大量没有装配指令优化器的互联网服务提供商(ISPs)和服务器。
32、并不是事必面向对象(OOP),面向对象往往开销很大,每个方法和对象调用都会消耗很多内存。
33、并非要用类实现所有的数据结构,数组也很有用。
34、不要把方法细分得过多,仔细想想你真正打算重用的是哪些代码?
35、当你需要时,你总能把代码分解成方法。
36、尽量采用大量的PHP内置函数。
37、如果在代码中存在大量耗时的函数,你可以考虑用C扩展的方式实现它们。
38、评估检验(profile)你的代码。检验器会告诉你,代码的哪些部分消耗了多少时间。Xdebug调试器包含了检验程序,评估检验总体上可以显示出代码的瓶颈。
39、mod_zip可作为Apache模块,用来即时压缩你的数据,并可让数据传输量降低80%。
40、在可以用file_get_contents替代file、fopen、feof、fgets等系列方法的情况下,尽量用 file_get_contents,因为他的效率高得多!但是要注意file_get_contents在打开一个URL文件时候的PHP版本问题;
41、尽量的少进行文件操作,虽然PHP的文件操作效率也不低的;
42、优化Select SQL语句,在可能的情况下尽量少的进行Insert、Update操作(在update上,我被恶批过);
43、尽可能的使用PHP内部函数(但是我却为了找个PHP里面不存在的函数,浪费了本可以写出一个自定义函数的时间,经验问题啊!);
44、循环内部不要声明变量,尤其是大变量:对象(这好像不只是PHP里面要注意的问题吧?);
45、多维数组尽量不要循环嵌套赋值;
46、在可以用PHP内部字符串操作函数的情况下,不要用正则表达式;
47、foreach效率更高,尽量用foreach代替while和for循环;
48、用单引号替代双引号引用字符串;
49、”用i+=1代替i=i+1。符合c/c++的习惯,效率还高”;
50、对global变量,应该用完就unset()掉。
没有更多推荐了,java中两个字符串的内存地址相同_百度知道
java中两个字符串的内存地址相同
既然两个字符串的内存地址相同,那么为什么改动一个字符串的内容,另一个字符串不随之改变
public static void main(String[] args) {
String s1 = new String(&I am a student&);
String s4 = s1;
if(s1 == s4) {
System.out.println(&这两个字...
我有更好的答案
String s1 = new String(&I am a student&);
你声明了一个引用
s1指向的是
new String(&I am a student&);
这个字符串String s4 = s1;
这里 你又声明一个引用 指向s1的引用 也就是new String(&I am a student&); if(s1 == s4) {
System.out.println(&这两个字符串的内存位置相同&);
}上面还是相等的但是 s4 = s4.replace('a', 'A');s4.replace('a', 'A');
生成了另一个 字符串
是final类型的
所以这个时候
即原来已经开辟了内存空间的
new String(&I am a student&); 是不可能改变内容了的这个时候 s4.replace('a', 'A');
就另开辟了一个内存空间 而这个时候 你的S4指向s4.replace('a', 'A');
而原来的s1还是指向 new String(&I am a student&); 他们肯定不一样的呢
采纳率:64%
已经有人回答了,那我就顺便贴一点java内存分配的小知识吧:Java内存分配与管理是Java的核心技术之一,之前我们曾介绍过Java的内存管理与内存泄露以及Java垃圾回收方面的知识,今天我们再次深入Java核心,详细介绍一下Java在内存分配方面的知识。一般Java在内存分配时会涉及到以下区域:  ◆寄存器:我们在程序中无法控制  ◆栈:存放基本类型的数据和对象的引用,但对象本身不存放在栈中,而是存放在堆中(new 出来的对象)  ◆堆:存放用new产生的数据  ◆静态域:存放在对象中用static定义的静态成员  ◆常量池:存放常量  ◆非RAM存储:硬盘等永久存储空间Java内存分配中的栈  在函数中定义的一些基本类型的变量数据和对象的引用变量都在函数的栈内存中分配。  当在一段代码块定义一个变量时,Java就在栈中 为这个变量分配内存空间,当该变量退出该作用域后,Java会自动释放掉为该变量所分配的内存空间,该内存空间可以立即被另作他用。栈中的数据大小和生命周期是可以确定的,当没有引用指向数据时,这个数据就会消失。Java内存分配中的堆  堆内存用来存放由new创建的对象和数组。 在堆中分配的内存,由Java虚拟机的自动垃圾回收器来管理。  在堆中产生了一个数组或对象后,还可以 在栈中定义一个特殊的变量,让栈中这个变量的取值等于数组或对象在堆内存中的首地址,栈中的这个变量就成了数组或对象的引用变量。
引用变量就相当于是 为数组或对象起的一个名称,以后就可以在程序中使用栈中的引用变量来访问堆中的数组或对象。引用变量就相当于是为数组或者对象起的一个名称。  引用变量是普通的变量,定义时在栈中分配,引用变量在程序运行到其作用域之外后被释放。而数组和对象本身在堆中分配,即使程序 运行到使用 new 产生数组或者对象的语句所在的代码块之外,数组和对象本身占据的内存不会被释放,数组和对象在没有引用变量指向它的时候,才变为垃圾,不能在被使用,但仍 然占据内存空间不放,在随后的一个不确定的时间被垃圾回收器收走(释放掉)。这也是 Java 比较占内存的原因。  实际上,栈中的变量指向堆内存中的变量,这就是Java中的指针! 堆与栈  Java的堆是一个运行时数据区,类的(对象从中分配空间。这些对象通过new、newarray、 anewarray和multianewarray等指令建立,它们不需要程序代码来显式的释放。堆是由垃圾回收来负责的,堆的优势是可以动态地分配内存 大小,生存期也不必事先告诉编译器,因为它是在运行时动态分配内存的,Java的垃圾收集器会自动收走这些不再使用的数据。但缺点是,由于要在运行时动态 分配内存,存取速度较慢。  栈的优势是,存取速度比堆要快,仅次于寄存器,栈数据可以共享。但缺点是,存在栈中的数据大小与生存期必须是 确定的,缺乏灵活性。栈中主要存放一些基本类型的变量数据(int, short, long, byte, float, double, boolean, char)和对象句柄(引用)。
栈有一个很重要的特殊性,就是存在栈中的数据可以共享。假设我们同时定义:  Java代码  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, 它是由编译器完成的,它有利于节省空间。而一个对象引用变量修改了这个对象的内部状态,会影响到另一个对象引用变量。Java代码 1.int i1 = 9;
2.int i2 = 9;
3.int i3 = 9;
4.public static final int INT1 = 9;
5.public static final int INT2 = 9;
6.public static final int INT3 = 9;
对于成员变量和局部变量:成员变量就是方法外部,类的内部定义的变量;局部变量就是方法或语句块内部定义的变量。局部变量必须初始化。 形式参数是局部变量,局部变量的数据存在于栈内存中。栈内存中的局部变量随着方法的消失而消失。 成员变量存储在堆中的对象里面,由垃圾回收器负责回收。 如以下代码: Java代码 1.class BirthDate {
public BirthDate(int d, int m, int y) {
省略get,set方法………
13.public class Test{
public static void main(String args[]){
15.int date = 9;
Test test = new Test();
test.change(date);
BirthDate d1= new BirthDate(7,7,1970);
public void change1(int i){
对于以上这段代码,date为局部变量,i,d,m,y都是形参为局部变量,day,month,year为成员变量。下面分析一下代码执行时候的变化: 1. main方法开始执行:int date = 9; date局部变量,基础类型,引用和值都存在栈中。 2. Test test = new Test(); test为对象引用,存在栈中,对象(new Test())存在堆中。 3. test.change(date); i为局部变量,引用和值存在栈中。当方法change执行完成后,i就会从栈中消失。 4. BirthDate d1= new BirthDate(7,7,1970);
d1 为对象引用,存在栈中,对象(new BirthDate())存在堆中,其中d,m,y为局部变量存储在栈中,且它们的类型为基础类型,因此它们的数据也存储在栈中。 day,month,year为成员变量,它们存储在堆中(new BirthDate()里面)。当BirthDate构造方法执行完之后,d,m,y将从栈中消失。5.main方法执行完之后,date变量,test,d1引用将从栈中消失,new Test(),new BirthDate()将等待垃圾回收。常量池 (constant pool)  常量池指的是在编译期被确定,并被保存在已编译的.class文件中的一些数据。除了包含代码中所定义的各种基本类型(如int、long等等)和对象型(如String及数组)的常量值(final)还包含一些以文本形式出现的符号引用,比如:  ◆类和接口的全限定名;  ◆字段的名称和描述符;  ◆方法和名称和描述符。如果是编译期已经创建好(直接用双引号定义的)的就存储在常量池中,如果是运行期(new出来的)才能确定的就存储在堆中。对于equals相等的字符串,在常量池中永远只有一份,在堆中有多份。String是一个特殊的包装类数据。可以用:  Java代码  String str = new String(&abc&);  String str = &abc&;  两种的形式来创建,第一种是用new()来新建对象的,它会在存放于堆中。每调用一次就会创建一个新的对象。而第二种是先在栈中创建一个对 String类的对象引用变量str,然后通过符号引用去字符串常量池 里找有没有&abc&,如果没有,则将&abc&存放进字符串常量池 ,并令str指向”abc”,如果已经有”abc” 则直接令str指向“abc”。  比较类里面的数值是否相等时,用equals()方法;当测试两个包装类的引用是否指向同一个对象时,用==,下面用例子说明上面的理论。  Java代码  String str1 = &abc&;  String str2 = &abc&;  System.out.println(str1==str2); //true  可以看出str1和str2是指向同一个对象的。  Java代码  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常量池问题的几个例子示例1:Java代码  String s0=&kvill&;  String s1=&kvill&;  String s2=&kv& + &ill&;  System.out.println( s0==s1 );  System.out.println( s0==s2 );  结果为:  true  true分析:首先,我们要知结果为道Java 会确保一个字符串常量只有一个拷贝。  因为例子中的 s0和s1中的”kvill”都是字符串常量,它们在编译期就被确定了,所以s0==s1为true;而”kv”和”ill”也都是字符串常量,当一个字 符串由多个字符串常量连接而成时,它自己肯定也是字符串常量,所以s2也同样在编译期就被解析为一个字符串常量,所以s2也是常量池中” kvill”的一个引用。所以我们得出s0==s1==s2;示例2:示例:  Java代码  String s0=&kvill&;  String s1=new String(&kvill&);  String s2=&kv& + new String(&ill&);  System.out.println( s0==s1 );  System.out.println( s0==s2 );  System.out.println( s1==s2 );  结果为:  false  false  false分析:用new String() 创建的字符串不是常量,不能在编译期就确定,所以new String() 创建的字符串不放入常量池中,它们有自己的地址空间。s0还是常量池 中&kvill”的应用,s1因为无法在编译期确定,所以是运行时创建的新对象”kvill”的引用,s2因为有后半部分 new String(”ill”)所以也无法在编译期确定,所以也是一个新创建对象”kvill”的应用;明白了这些也就知道为何得出此结果了。示例3:Java代码  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分析:JVM对于字符串常量的&+&号连接,将程序编译期,JVM就将常量字符串的&+&连接优化为连接后的值,拿&a& + 1来说,经编译器优化后在class中就已经是a1。在编译期其字符串常量的值就确定下来,故上面程序最终的结果都为true。示例4:Java代码  String a = &ab&;  String bb = &b&;  String b = &a& +  System.out.println((a == b)); //result = false分析:JVM对于字符串引用,由于在字符串的&+&连接中,有字符串引用存在,而引用的值在程序编译期是无法确定的,即&a& + bb无法被编译器优化,只有在程序运行期来动态分配并将连接后的新地址赋给b。所以上面程序的结果也就为false。示例5:Java代码  String a = &ab&;  final String bb = &b&;  String b = &a& +  System.out.println((a == b)); //result = true分析:和[4]中唯一不同的是bb字符串加了final修饰,对于final修饰的变量,它在编译时被解析为常量值的一个本地拷贝存储到自己的常量 池中或嵌入到它的字节码流中。所以此时的&a& + bb和&a& + &b&效果是一样的。故上面程序的结果为true。示例6:Java代码  String a = &ab&;  final String bb = getBB();  String b = &a& +  System.out.println((a == b)); //result = false  private static String getBB() {
return &b&;
}分析:JVM对于字符串引用bb,它的值在编译期无法确定,只有在程序运行期调用方法后,将方法的返回值和&a&来动态连接并分配地址为b,故上面 程序的结果为false。关于String是不可变的
通过上面例子可以得出得知:  String
&a& + &b& + &c&;  就等价于String s = &abc&;  String
&a&;  String
&b&;  String
&c&;  String
+  这个就不一样了,最终结果等于:  Java代码  StringBuffer temp = new StringBuffer();  temp.append(a).append(b).append(c);  String s = temp.toString();  由上面的分析结果,可就不难推断出String 采用连接运算符(+)效率低下原因分析,形如这样的代码:  Java代码  public class Test {  
public static void main(String args[]) {  
String s =  
for(int i = 0; i & 100; i++) {  
s += &a&;  
}   }  }  每做一次 + 就产生个StringBuilder对象,然后append后就扔掉。下次循环再到达时重新产生个StringBuilder对象,然后 append 字符串,如此循环直至结束。如果我们直接采用 StringBuilder 对象进行 append 的话,我们可以节省 N - 1 次创建和销毁对象的时间。所以对于在循环中要进行字符串连接的应用,一般都是用StringBuffer或StringBulider对象来进行 append操作。  由于String类的immutable性质,这一说又要说很多,大家只 要知道String的实例一旦生成就不会再改变了,比如说:String str=”kv”+”ill”+” “+”ans”; 就是有4个字符串常量,首先”kv”和”ill”生成了”kvill”存在内存中,然后”kvill”又和” ” 生成 “kvill “存在内存中,最后又和生成了”kvill ans”;并把这个字符串的地址赋给了str,就是因为String的”不可变”产生了很多临时变量,这也就是为什么建议用StringBuffer的原 因了,因为StringBuffer是可改变的。String中的final用法和理解  Java代码  final StringBuffer a = new StringBuffer(&111&);  final StringBuffer b = new StringBuffer(&222&);  a=b;//此句编译不通过  final StringBuffer a = new StringBuffer(&111&);  a.append(&222&);// 编译通过  可见,final只对引用的&值&(即内存地址)有效,它迫使引用只能指向初始指向的那个对象,改变它的指向会导致编译期错误。至于它所指向的对象 的变化,final是不负责的。总结  栈中用来存放一些原始数据类型的局部变量数据和对象的引用(String,数组.对象等等)但不存放对象内容  堆中存放使用new关键字创建的对象.  字符串是一个特殊包装类,其引用是存放在栈里的,而对象内容必须根据创建方式不同定(常量池和堆).有的是编译期就已经创建好,存放在字符串常 量池中,而有的是运行时才被创建.使用new关键字,存放在堆中。
java 内存这一块水很深,我们就说说字符串,首先 String s1 = new String(&I am a student&); 和 String s2 = &I am a student&;这就是两个不同的概念, 前者分配的是堆内存,后者分配的是栈内存,这两个地址肯定不会相等。同样 String s1 = new String(&I am a student&); 和 String s2 = new String(&I am a student&); 这两个都是分配的堆内存,不是同一个对象,地址也不相等的。String 对象跟8大基本对象一样也有常量池空间,String s1 = &I am a student&; 和 String s2 = &I am a student&; 像这种两个对象他们的内存地址相等。我只是开了一个头,关于这一块,你需要仔细看看java 内存管理这一块。
String 是不可变量,免疫的,可以认为相当于C++里面的constant变量问题在这一行:s4 = s4.replace('a', 'A');replace会返回一个新的字符串给s4,此时s1和s4不但内容不同,指向的变量也不同。
String s4 = new String(&I am a student&);
这样子结果就完全不一样了,试试比较比较?
看了这个,你就明白了。
其他3条回答
为您推荐:
其他类似问题
内存地址的相关知识
换一换
回答问题,赢新手礼包
个人、企业类
违法有害信息,请在下方选择后提交
色情、暴力
我们会通过消息、邮箱等方式尽快将举报结果通知您。

我要回帖

更多关于 二维字符数组 的文章

 

随机推荐