Java字符串处理String、StringBuilder、StringBuffer类效率分析

作者:小菜 更新时间:2025-02-26 点击数:
简介:字符串操作是编写程序中最常见的行为,本文对String、StringBuilder、StringBuffer三个类在字符串处理方面的效率进行分析。

Java中最常

【菜科解读】

字符串操作是编写程序中最常见的行为,本文对String、StringBuilder、StringBuffer三个类在字符串处理方面的效率进行分析。

Java中最常见也是应用最广泛的类就是String类。

String:Strings are constant; their values cannot be changed after they are created.

这是JDK对String的解释,意思是:String是常量,一旦创建后它的值不能被修改。

先看String对象是如何创建的:

String str1 = “abc”;

String str2 = new String(“abc”);

这是我们常见的两种形式。

第一种方式创建的字符串会放在栈里,更确切的是常量池中,常量池就是用来保存在编译阶段确定好了大小的数据,一般我们定义的int等基本数据类型就保存在这里。

其具体的一个流程就是,编译器首先检查常量池,看看有没有一个“abc”,如果没有则创建。

如果有的话,则则直接把str1指向那个位置。

第二种创建字符串的方法是通过new关键字,还是java的内存分配,java会将new的对象放在堆中,这一部分对象是在运行时创建的对象。

所以我们每一次new的时候,都会创建不同的对象,即便是堆中已经有了一个一模一样的。

下面的程序将验证上面的说法。

1 String str1 = "abc";2 String str2 = new String("abc");3 4 String str3 = "abc";5 String str4 = new String("abc");6 7 System.out.println(str1==str2);//false8 9 System.out.println(str1 == str3);//true10 System.out.println(str2 == str4);//false11 // When the intern method is invoked, if the pool already contains a12 // string equal to this String object as determined by the13 // equals(Object) method, then the string from the pool is returned.14 // Otherwise, this String object is added to the pool and a reference to15 // this String object is returned.16 str2 = str2.intern();17 System.out.println(str1==str2);//true18 19 false20 true21 false22 true

以上程序中用两种方式创建的4个对象,”==”比较的是地址,从结果可以看到str1和str3相同,指向同一个对象。

而str2和str4比较返回结果是false,说明str2和str4指向的不是同一个对象。

(上面用到了一个比较少见的方法:intern。

在str2调用了intern方法后对str1和str2进行比较返回true,可以从代码注释中看明白,intern方法返回常量池中内容和该对象相同的对象,如果常量池中不存在,则将该对象加入到常量池中。

String对象是不变的,那为什么可以进行str+=”abc”这样的操作?其实类似这样的操作是新生成了一个String对象,包含str和abc连接后的字符串。

1 package test;2 3 public class StringTest {4public static void main(String[] args) {5 String str1 = "abc";6 str1 += "123";7}8 }

从编译之后的class可以看出编译器自动引入了StringBuilder类,通过它的初始化方法创建了StringBuilder对象,通过append方法添加了内容,调用toString返回连接后的对象。

以上内容证明了String对象是不可变的,连接操作实际是返回了新的对象。

如果是循环多次进行连接,将不断的创建StringBuilder对象,append新内容,toString成String对象。

这就带来了String类处理字符串更改操作的效率问题。

1 public class StringTest {2public static void main(String[] args) {3 long start, end;4 5 StringBuilder strBuilder = new StringBuilder(" ");6 start = System.currentTimeMillis();7 for (int i = 0; i 可以看到String和StringBuilder在效率方便的差距。

StringBuffer:A thread-safe, mutable sequence of characters. A string buffer is like a String, but can be modified.

StringBuilder:A mutable sequence of characters. This class provides an API compatible with StringBuffer, but with no guarantee of synchronization.

StringBuffer是可变的、线程安全的字符串。

StringBuilder就是StringBuffer的非线程同步的版本,二者的方法差不多,只是一个线程安全(适用于多线程)一个没有线程安全(适用于单线程)。

Java,字符串,处理,String,、,StringBui

java多线程编程小结

每个Java程序都有一个默认的主线程。

Java程序总是从主类的main方法开始执行。

当JVM加载代码,发现main方法后就启动一个线程,这个线程就称作"主线程",该线程负责执行main方法。

在main方法中再创建的线程就是其他线程。

如果main方法中没有创建其他线程,那么当main方法返回时JVM就会结束Java应用程序。

但如果main方法中创建了其他线程,那么JVM就要在主线程和其他线程之间轮流切换,保证每个线程都有机会使用CPU资源,main方法返回(主线程结束)JVM也不会结束,要一直等到该程序所有线程全部结束才结束Java程序(另外一种情况是:程序中调用了Runtime类的exit方法,并且安全管理器允许退出操作发生。

这时JVM也会结束该程序)。

线程的常用方法:start():线程调用该方法将启动线程,从新建态进入就绪队列,一旦享用CPU资源就可以脱离创建它的线程,独立开始自己的生命周期。

run():Thread类的run()方法与Runnable接口中的run()方法功能和作用相同,都用来定义线程对象被调度后所进行的操作,都是系统自动调用而用户不得引用的方法。

run()方法执行完毕,线程就成死亡状态,即线程释放了分配给它的内存(死亡态线程不能再调用start()方法)。

在线程没有结束run()方法前,不能让线程再调用start()方法,否则将发生IllegalThreadStateException异常。

sleep(int millsecond):有时,优先级高的线程需要优先级低的线程做一些工作来配合它,此时为让优先级高的线程让出CPU资源,使得优先级低的线程有机会运行,可以使用sleep(int millsecond)方法。

线程在休眠时被打断,JVM就抛出InterruptedException异常。

因此,必须在try-catch语句块中调用sleep方法。

isAlive():当线程调用start()方法并占有CPU资源后该线程的run()方法开始运行,在run()方法没有结束之前调用isAlive()返回true,当线程处于新建态或死亡态时调用isAlive()返回false。

注意:一个已经运行的线程在没有进入死亡态时,不要再给它分配实体,由于线程只能引用最后分配的实体,先前的实体就成为了"垃圾",并且不能被垃圾回收机制收集。

currentThread():是Thread类的类方法,可以用类名调用,返回当前正在使用CPU资源的线程。

interrupt():当线程调用sleep()方法处于休眠状态,一个占有CPU资源的线程可以让休眠的线程调用interrupt()方法"吵醒"自己,即导致线程发生IllegalThreadStateException异常,从而结束休眠,重新排队等待CPU资源。

GUI线程:JVM在运行包含图形界面应用程序时,会自动启动更多线程,其中有两个重要的线程:AWT-EventQueue和AWT-Windows。

AWT-EventQueue线程负责处理GUI事件,AWT-Windows线程负责将窗体或组件绘制到桌面。

线程同步:(用synchronized修饰某个方法,该方法修改需要同步的变量;或用volatile修饰基本变量)当两个或多个线程同时访问一个变量,并且一个线程需要修改这个变量时,应对这样的问题进行处理,否则可能发生混乱。

要处理线程同步,可以把修改数据的方法用关键字synchronized修饰。

一个方法使用synchronized修饰,当一个线程A使用这个方法时,其他线程想使用该方法时就必须等待,直到线程A使用完该方法。

所谓同步就是多个线程都需要使用一个synchronized修饰的方法。

volatile比同步简单,只适合于控制对基本变量(整数、布尔变量等)的单个实例的访问。

java中的volatile关键字与C++中一样,用volatile修饰的变量在读写操作时不会进行优化(取cache里的值以提高io速度),而是直接对主存进行操作,这表示所有线程在任何时候看到的volatile变量值都相同。

在同步方法中使用wait()、notify()、notifyAll()方法:当一个线程使用的同步方法中用到某个变量,而此变量又需要其他线程修改后才能符合本线程需要,那么可以在同步方法中使用wait()方法。

中断方法的执行,使本线程等待,暂时让出CPU资源,并允许其他线程使用这个同步方法。

其他线程如果在使用这个同步方法时不需要等待,那么它使用完这个同步方法时应当用notifyAll()方法通知所有由于使用这个同步方法而处于等待的线程结束等待。

曾中断的线程就会从中断处继续执行,并遵循"先中断先继续"的原则。

如果用的notify()方法,那么只是通知等待中的线程中某一个结束等待。

计时器线程Timer:(Timer还有很多高级操作,详细见JDK,这里做个概述)java.swing.Timer类用于周期性地执行某些操作。

有两个常用构造函数public Timer(int delay, ActionListener listener):参数listener是计时器的监视器,计时器发生振铃的事件是ActionEvent类型事件,当振铃事件发生,监视器会监视到这个事件并回调ActionListener接口中的actionPerformed(ActionEvent e)方法。

public Timer(int delay):使用该构造方法,计时器要再调用addActionListener(ActionListener listener)方法获得监视器。

如果想让计时器只震动一次,可以让计时器调用setRepeats(boolean b)方法,参数b取false即可。

计时器还可以调用setInitialDelay(int delay)方法设置首次振铃的延时,如果没有设置首次振铃默认延时为构造函数中的参数delay。

还可以调用getDelay()和setDelay(int delay)获取和设置延时。

计时器创建后调用start()启动,调用stop()停止,即挂起,调用restart()重新启动计时器,即恢复线程。

java有点不同,实现多线程有两种方式:继承类Thread, 和 实现接口Runnable。

thread类有一个run函数,它是线程的入口,当启动一个新线程是,就从这个函数开始执行;public class ThreadTest extends Thread{ public void run() for (int i=0;i注意线程是调用start()来开始的。

Runnable有一点点不同,实现这个类的时候和Thread一样,但是创建线程的时候还是放入一个Thread中创建:public class RunnableTest implements Runnable private int cnt; public RunnableTest(int n) super(); cnt = n; public void run() for (int i=0;ijoin(): 强制等待线程执行完毕。

例如如果在主线程里面加一句ta.join(),那么主线程会一直等待ta执行返回才接着执行后面的代码。

但是在join之前已经创建的其他线程,则不会受影响,继续执行。

interrupt(): 挺有意思的一个函数,如果线程在执行sleep()函数 ,则打断它(以让sleep()函数抛出一个异常的方式中断),执行后面的语句。

setDaemon(true):设置为守护进程。

守护进程的优先级是最低的,如果一个程序只剩下守护进程,那么它就中断所有守护进程而退出。

最常见的情况是,如果创建的全部是守护进程,那么当主函数main执行完毕后,就立刻终止所有线程而退出。

synchronized同步符号用法: 保证同一时刻,某段代码只有一个线程在执行。

wait()和notify(),notifyAll():让线程等待/唤醒。

这两个函数比较奇怪,目前我不是很熟练,要配合synchronized符号使用,请看下面这个生产者-消费者例子(网上直接贴来的,写的挺好的):public class tt { public static void main(String[] args) { Storage s = new Storage(); Producer p = new Producer(s); Consumer c = new Consumer(s); Thread tp = new Thread(p); Thread tc = new Thread(c); tp.start(); tc.start();class Consumer implements Runnable {//消费者 Storage s = null; public Consumer(Storage s){ this.s = s; public void run() { for(int i=0; i java,多,线程,编程,小结,每个,Java,程序,都,

Java页面设计之事件处理

在JAVA程序设计中,事件的处理是非常重要的,尤其是在需要自定义事件和设计JavaBean时.对事件的处理过程有一个完整的认识对于编程是很有帮助的。

一、事件:用户对程序的某一种功能性操作Java中的事件主要有两种:1.组件类事件componentEvent、ContainerEvent、WindowEvent、FocusEvent、PaintEvent、MouseEvent共六大类,它们均是当组件的状态发生变化时产生。

2.动作类事件ActionEvent、TextEvent、AdjustmentEvent、ItemEvent共四类。

Java中的事件类都包含在JDK的Java.awt.event包中。

二、事件编程:用户编程定义每个特定事件发生时程序应做出何种响应,并且这些响应代码会在对应的事件发生时由系统自动调用。

如果想要自定义一个事件,则必须提供一个事件的监听接口以及一个事件类。

在JAVA中监听接口继承java.util.EventListener,事件类继承java.util.EventObject.很多基本的事件在编程环境中都已经提供可以很方便使用,但是在自定义事件中必须要要了解这些。

当你把界面都设计好了,总需要添加相应的执行动作给组件,在JAVA中有相应的时间处理机制,叫做“监听器”,给组件添加相应执行动作的过程叫做“注册”,其中的“监听器”是一个接口,里面包含了相应的执行函数,要做的是自己实现其中的方法函数,然后“注册”给组件,通俗一点的话就是老妈让我去买酱油,老妈不关心我会转过几天街道,会怎样和老板到讨价还价怎样回家,老妈需要的最终结果是我能够将酱油交付到她的手中,期中买酱油的过程是我实现的,我就是事件处理机制里面的“监听器”接受老妈的指示,老妈让我买酱油去,就是对应将事件“注册”给我,呵呵,又一次完美体现了JAVA中实现与接口分离。

JAVA中的组件事件有ActionEvent、KeyEvent、FocusEvent、ComponentEvent、MouseEvent、AdjustmentEvent等,每个组件支持其中的全部或者是一部分事件,相应的事件有相应的Listener来监听事件的发生并且实现接口的方法,程序员要做的事情就是创建一个事件的类对象,实现它里面的函数,然后将它注册给相应的组件,下面用代码演示一下:1 import javax.swing.*;2 import java.awt.*;3 import java.awt.event.*;4 5 public class ButtonDemo {6 private JFrame 7 frame=new JFrame("ButtonDemo");8 private JButton9 b1 = new JButton("按钮1"),10 b2 = new JButton("按钮2");11 private JTextField 12 txt = new JTextField(10);13 //这里是使用一个匿名类来实现对按钮的监听14 private ActionListener bl = new ActionListener() {15 //实现监听类中的抽象函数16 public void actionPerformed(ActionEvent e) {17 String name = ((JButton)e.getSource()).getText();18 txt.setText(name);19 }20 };21 public ButtonDemo () {22 //将监听对象注册给两个按钮23 b1.addActionListener(bl); 24 b2.addActionListener(bl);25 frame.setLayout(new FlowLayout());26 frame.add(b1);27 frame.add(b2);28 frame.add(txt);29 frame.setVisible(true);30 frame.setSize(200,150);31 32 }33 public static void main(String[] args) {34 new ButtonDemo (); 35 }36 } 监听接口中如果有不止一个函数,而我只想实现其中的一个函数的时候,很显示程序是不能运行的,因为你必须实现接口当中的所有函数的时候,才可以通过编译,程序才可以跑起来,那应该怎们办纳?呵呵,显然JAVA语言设计者已经考虑到了这一点,所以提供了一个叫做“适配器”的家伙,它默认实现了接口当中的所有函数,继承“适配器”这个类,并覆盖你所该兴趣的那个函数就可以了: 1 class MyMouseListener extends MouseAdapter{ 2 public void mouseClicked(MouseEvent e){ 3 //实现当点击鼠标时的动作 4 } 5 } Java,页面,设计,之,事件,处理,在,JAVA,程序设计

加入收藏
               

Java字符串处理String、StringBuilder、StringBuffer类效率分析

点击下载文档

格式为doc格式

  • 账号登录
社交账号登录