Java学习笔记——String

一、String创建方式

1.构造方法

      使用new关键字创建字符串对象。

String str = new String("123");

2.字面量

      类似于直接赋值的方式。

String str = "123";

      字面量与构造方法创建出来的字符串是有区别的,下面将介绍两者之间的区别。通过new创建出来的字符串会存储在堆里,并且有自己的空间。
      通过字面量创建出来的字符串会被放到字符串常量池中,如果字符串常量池中没有相同内容的字符串,会先在字符串常量池中创建该字符串,然后将引用地址返回变量,如下图所示。
在这里插入图片描述

      如果字符串常量池中有相同内容的字符串,则返回已经存在的字符串的引用给变量。
      为了便于理解,举了下文的例子。从图中可以看出,s4和s5所指对象是不同的,而对象内容是相同的,所以第一个输出为false,第二个输出为true。

String s1 = "Runnoob";  //String直接创建
String s2 = "Runnoob";  //String直接创建
String s3 = s1;         //相同引用
String s4 = new String("Runnoob");  //String对象创建
String s5 = new String("Runnoob");  //String对象创建
System.out.println(s4==s5);  //false
System.out.println(s4.equals(s5)); //true

在这里插入图片描述
      既然两种创建方式存储的位置不同,那我有没有什么方法能将堆中的String对象放到字符串常量池中呢?当然有,使用intern()方法,例子如下。

String str1 = "Stack to pool";
String str2 = new String("Stack to pool");
System.out.println(str1 == str2); //false
System.out.println(str1 == str2.intern()); //true

      为什么要将堆中的String对象放到字符串常量池中呢?因为这样可以提升内存使用效率,同时让使用者共享唯一的实例。intern()方法的大致流程是:
      1、先判断字符串常量池中是否含有相同(通过equals方法)的字符串字面量,如果有直接返回字符串字面量;
      2、如果不含,则将该字符串对象添加到字符串常量池中,同时返回该对象在字符串常量池的引用。返回的引用需要赋值才可,否则还是会指向堆中的地址,即:

String str1 = "Stack to pool";
String str2 = new String("Stack to pool");
System.out.println(str1 == str2.intern()); //true
System.out.println(str1 == str2); //false
str2 = str2.intern();
System.out.println(str1 == str2); //true

二、String特点

1. 不可变性

      String对象一旦在堆中创建出来,就无法再修改。那是因为String对象放在char数组中,该数组由final关键字修饰,不可变。注意:String不可变不是因为自身被final修饰,而是因为它底层存储是一个不可变的数组,程序无法对它进行修改。

/**
 * The {@code String} class represents character strings. All
 * string literals in Java programs, such as {@code "abc"}, are
 * implemented as instances of this class.
 * <p>
 * Strings are constant; their values cannot be changed after they
 * are created. String buffers support mutable strings.
 * Because String objects are immutable they can be shared. For example:
 * ...
 */

public final class String { 
	private final char value[];
}

      如果把新的值赋给已经定义的字符串,此时不可变性是如何体现的呢?可以看出是不改变原来的字符串Hello future,而是创建了新的字符串Hello。

String str1 = new String("Hello world");
String str2 = "Hello world";
String str3 = "Hello future";
str3 = "Hello";

在这里插入图片描述

2.安全性

      因为是不可变对象,所以String是线程安全的。

三、String使用

1.拼接

      通过String变量 + 字符串常量方式得到的结果会在堆中,不在常量池中,当然可以通过intern()方法放进常量池中,同时不仅"+"如此,调用substring(),toUpperCase(),trim()等返回的都是String在堆中的地址。

String str1 = "good good";
String str2 = str1 + "study";
String str3 = "good good study";
System.out.println(str2 == str3); //false

2.不可变性的不足

      不可变性最大的不足是任何修改都会产生新的对象,而新的对象就会引起创建对象的开销,解决方法是使用StringBuffer或StringBuilder。首先看下面的一个例子

    @Test
    public void testPerformance(){
        String str0 = "hello,world";

        long start = System.currentTimeMillis();
        for (int i = 0; i < 100000; i++) {
            str0 += i;
        }
        System.out.println(System.currentTimeMillis() - start);  //38833

        StringBuilder sb = new StringBuilder("hello,world");
        long start1 = System.currentTimeMillis();
        for (int i = 0; i < 100000; i++) {
            sb.append(i);
        }
        System.out.println(System.currentTimeMillis() - start1); //4

        StringBuffer sbf = new StringBuffer("hello,world");
        long start2 = System.currentTimeMillis();
        for (int i = 0; i < 100000; i++) {
            sbf.append(i);
        }
        System.out.println(System.currentTimeMillis() - start2); //4
    }

      可以看出执行时间差别很大,为了解决String不擅长的大量字符串拼接这种业务场景,所以我们引入了StringBuffer和StringBuilder。
      StringBuffer与StringBuilder的允许时间基本一致,那为什么需要定义两个功能相似的类呢?

    @Test
    public void testSafe() throws InterruptedException {
        String str0 = "hello,world";

        StringBuilder sb = new StringBuilder(str0);
        for (int i = 0; i < 100; i++) {
            new Thread(() -> {
                try {
                    TimeUnit.MILLISECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                sb.append("a");
            }).start();
        }


        StringBuffer sbf = new StringBuffer(str0);
        for (int i = 0; i < 100; i++) {
            new Thread(() -> {
                try {
                    TimeUnit.MILLISECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                sbf.append("a");
            }).start();
        }
        // 等待工作线程运行结束
        while (Thread.activeCount()>2){

        }
        System.out.println("StringBuilder:"+sb.toString().length()); //109
        System.out.println("StringBuffer:"+sbf.toString().length()); //111
    }

      本来拼接完程度应该是111的,但是现在呢,StringBuilder是109,说明了StringBuilder不是安全的,也就是说在多线程的环境下,我们应该使用StringBuffer。
      下面我们对比了String、StringBuffer与StringBuilder的区别

StringStringBufferStringBuilder
final修饰,不可继承final修饰,不可继承final修饰,不可继承
字符串常量,创建后不可变字符串变量,可动态修改字符串变量,可动态修改
不存在线程安全问题线程安全,所有public方法由synchronized修改先从不安全
大量字符串拼接效率最低大量字符串拼接效率非常高大量字符串拼接效率最高

参考资料:
1.Java数据类型—String基础
2.Java数据类型—StringBuilder与StringBuffer
3.Java String 类

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
文件上传是Web开发中常见的功能之一,Java中也提供了多种方式来实现文件上传。其中,一种常用的方式是通过Apache的commons-fileupload组件来实现文件上传。 以下是实现文件上传的步骤: 1.在pom.xml文件中添加以下依赖: ```xml <dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>1.3.3</version> </dependency> ``` 2.在前端页面中添加文件上传表单: ```html <form method="post" enctype="multipart/form-data" action="upload"> <input type="file" name="file"> <input type="submit" value="Upload"> </form> ``` 3.在后台Java代码中处理上传文件: ```java // 创建一个DiskFileItemFactory对象,用于解析上传的文件 DiskFileItemFactory factory = new DiskFileItemFactory(); // 设置缓冲区大小,如果上传的文件大于缓冲区大小,则先将文件保存到临时文件中,再进行处理 factory.setSizeThreshold(1024 * 1024); // 创建一个ServletFileUpload对象,用于解析上传的文件 ServletFileUpload upload = new ServletFileUpload(factory); // 设置上传文件的大小限制,这里设置为10MB upload.setFileSizeMax(10 * 1024 * 1024); // 解析上传的文件,得到一个FileItem的List集合 List<FileItem> items = upload.parseRequest(request); // 遍历FileItem的List集合,处理上传的文件 for (FileItem item : items) { // 判断当前FileItem是否为上传的文件 if (!item.isFormField()) { // 获取上传文件的文件名 String fileName = item.getName(); // 创建一个File对象,用于保存上传的文件 File file = new File("D:/uploads/" + fileName); // 将上传的文件保存到指定的目录中 item.write(file); } } ``` 以上代码中,首先创建了一个DiskFileItemFactory对象,用于解析上传的文件。然后设置了缓冲区大小和上传文件的大小限制。接着创建一个ServletFileUpload对象,用于解析上传的文件。最后遍历FileItem的List集合,判断当前FileItem是否为上传的文件,如果是,则获取文件名,创建一个File对象,将上传的文件保存到指定的目录中。 4.文件上传完成后,可以给用户一个提示信息,例如: ```java response.getWriter().write("File uploaded successfully!"); ``` 以上就是使用Apache的commons-fileupload组件实现文件上传的步骤。需要注意的是,文件上传可能会带来安全隐患,因此在处理上传的文件时,需要进行严格的校验和过滤。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值