Java动态执行代码字符串

1. 步骤

假如我们需要动态执行一段字符串形式的java代码,大概需要这样几步:

  • 生成文件(具体的文件/内存中的文件)
  • 调用javac编译
  • 通过反射执行

2. 生成文件

  • 确定字符串的形式
    • 字符串已经是完整的.java形式,那么就不再需要再做操作,写入文件即可
    • 字符串只是一个方法,需要听过UUID为其包装出一个类名,再写入文件
    • 字符串只是一段字符串,就需要即为其加上方法名,又要加上类名,然后再写入文件

2.1 IO写入具体的磁盘文件

  • 阻塞字节流输出
public class ToFile {
    //字节流方式
    public static void main(String[] args) throws IOException {
        String code = new String("public class A{}");
        FileOutputStream outputStream = new FileOutputStream("E:\\spring\\dynamicJava\\src\\main\\resources\\T1.java");
        byte[] codeBytes = code.getBytes(StandardCharsets.UTF_8);
        outputStream.write(codeBytes);
        outputStream.close();
    }
}
  • 字符流输出
public class ToFileByWriter {
    //字符流方式
    public static void main(String[] args) throws IOException {
        String code = new String("public class B{}");
        FileWriter writer = new FileWriter("E:\\spring\\dynamicJava\\src\\main\\resources\\T2.java");
        writer.write(code);
        writer.flush();
        writer.close();
    }
}
  • RandomAccessFile的方式

RandomAccessFile 是 Java 输入/输出流体系中功能最丰富的文件内容访问类,它提供了众多的方法来访问文件内容,它既可以读取文件内容,也可以向文件输出数据。由于 RandomAccessFile可以从任意位置访问文件,所以在只需要访问文件部分内容的情况下,使用 RandonAccessFile 类是一个很好的选择。

RandomAccessFile 对象包含了一个记录指针,用以标识当前读写处的位置,当程序新创建一个 RandomAccessFile 对象时,该对象的文件记录指针位于文件头(也就是 0 处),当读/写了 n 个字节后,文件记录指针将会向后移动 n 个字节。除此之外,RandonAccessFile 可以自由移动该记录指针,既可以向前移动,也可以向后移动。

public class ToFileByRandomAccessFile {
    public static void main(String[] args) throws IOException {
        String code = new String("public class C{}");
        File file = new File("E:\\spring\\dynamicJava\\src\\main\\resources\\T3.java");
        RandomAccessFile accessFile = new RandomAccessFile(file,"rw");
        accessFile.write(code.getBytes());
        accessFile.close();
    }
}
  • NIO的方式
public class ToFileByNio {
    //通过javaNio的方式进行写入
    public static void main(String[] args) throws IOException {
        String code = new String("public class D{}");
        //1. 打开channel
        RandomAccessFile accessFile = new RandomAccessFile("E:\\spring\\dynamicJava\\src\\main\\resources\\T4.java", "rw");
        FileChannel channel = accessFile.getChannel();

        //2. 创建buffer对象并填入内容(buffer默认是写入模式)
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        buffer.put(code.getBytes(StandardCharsets.UTF_8));

        //3. 反转buffer
        buffer.flip();

        //4. 写入channel
        while (buffer.hasRemaining()) {
            channel.write(buffer);
        }

        //5. 关闭channel
        channel.close();
    }
}

2.2 生成内存的JavaFileObject对象

  • JavaFileObject源码
/**
 * File abstraction for tools operating on Java™ programming language
 * source and class files.
 *
 * <p>All methods in this interface might throw a SecurityException if
 * a security exception occurs.
 *
 * <p>Unless explicitly allowed, all methods in this interface might
 * throw a NullPointerException if given a {@code null} argument.
 *
 * @author Peter von der Ah&eacute;
 * @author Jonathan Gibbons
 * @see JavaFileManager
 * @since 1.6
 */
public interface JavaFileObject extends FileObject {
    enum Kind {
        SOURCE(".java"),
        CLASS(".class"),
        HTML(".html"),
        OTHER("");
        public final String extension;
        private Kind(String extension) {
            extension.getClass(); // null check
            this.extension = extension;
        }
    };
    Kind getKind();
    boolean isNameCompatible(String simpleName, Kind kind);
    NestingKind getNestingKind();
    Modifier getAccessLevel();
}

可以把一个JavaFileObject当成是一个不同类型文件在内存中的抽象。一般情况下,我们自己新建一个类完成SimpleJavaFileObject的创建,当然需要继承SimpleJavaFileObject

public class MySimpleJavaFileObject extends SimpleJavaFileObject {

    private String contents = null;
    private String className;

    public MySimpleJavaFileObject(String className, String contents) {
        super(URI.create("string:///" + className.replace('.', '/') + Kind.SOURCE.extension), Kind.SOURCE);
        this.className = className;
        this.contents = contents;
    }

    public CharSequence getCharContent(boolean ignoredEncodingErrors) throws IOException {
        return contents;
    }

    public String getClassName() {
        return className;
    }
}

3. 动态编译

由于编译对象的不同,具体的动态编译的输出形式也不同,可以实现:

  • 从源文件到字节码文件的编译方式
  • 从源文件到内存的编译方式
  • 从内存到内存的编译方式

3.1 JavaCompiler.run()

javax.tools包下的tools类中实现了一个run方法,如下:

/**
 * Common interface for tools that can be invoked from a program.
 * A tool is traditionally a command line program such as a compiler.
 * The set of tools available with a platform is defined by the
 * vendor.
 *
 * <p>Tools can be located using {@link
 * java.util.ServiceLoader#load(Class)}.
 *
 * @author Neal M Gafter
 * @author Peter von der Ah&eacute;
 * @author Jonathan Gibbons
 * @since 1.6
 */
public interface Tool {

    /**
     * Run the tool with the given I/O channels and arguments. By
     * convention a tool returns 0 for success and nonzero for errors.
     * Any diagnostics generated will be written to either {@code out}
     * or {@code err} in some unspecified format.
     *
     * @param in "standard" input; use System.in if null
     * @param out "standard" output; use System.out if null
     * @param err "standard" error; use System.err if null
     * @param arguments arguments to pass to the tool
     * @return 0 for success; nonzero otherwise
     * @throws NullPointerException if the array of arguments contains
     * any {@code null} elements.
     */
    int run(InputStream in, OutputStream out, OutputStream err, String... arguments);

    /**
     * Gets the source versions of the Java&trade; programming language
     * supported by this tool.
     * @return a set of supported source versions
     */
    Set<SourceVersion> getSourceVersions();

}

可以发现,Tool接口是一个可以从程序中调用的工具的公共接口,其中JavaCompiler(从程序中调用Java编程语言编译器的接口)Diagnostic(用于来自工具的诊断的接口)都是继承的他,其中有一个run()方法,接收参数由四个,分别为标准输入,标准输出,标准错误输出,最后一个参数为具体接口/实现类的参数。

JavaCompiler.run()类似于执行javac,第四个参数argument就是javac ./test.javatest.java。通过这样的方式可以在当前文件的目录下生成一个class文件。

public class CompilerRun {
    public static void main(String[] args) {
        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
        int run = compiler.run(null, null, null, "E:\\spring\\dynamicJava\\src\\main\\java\\com\\yuan\\compiler\\User.java");
        System.out.println("result = " + run);
    }
}

最终执行结果:

0表示编译成功,同时对应目录下出现相应的User.class

3.2 JavaCompiler.getTask()编译硬盘中的代码

使用JavaCompiler.run()方法非常简单,但它确不能更有效地得到我们所需要的信息。一般来说我们都会使用StandardJavaFileManager类(jdk 6或以上),这个类可以很好地控制输入、输出,并且可以通过DiagnosticListener得到诊断信息,而DiagnosticCollector类就是listener(监听)的实现。

需要注意的是 DiagnosticCollector在被解析的java文件没问题的情况下是不会收集信息的,我们这里引入一个lombok,相当于java文件中有不存在的包,可以发现以下的诊断信息:

执行前目录结构如下,目标是把编译后的结果都放置在下面的classes包中。

public class Test {
    public static void main(String[] args) throws IOException {
        Test.compiler();
    }

    public static void compiler() throws IOException {
        //1.获得系统编译器
        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();

        //2. 建立DiagnosticCollector对象
        DiagnosticCollector<Object> diagnosticCollector = new DiagnosticCollector<>();
        StandardJavaFileManager fileManager = compiler.getStandardFileManager(diagnosticCollector, null, null);

        //3. 建立源文件对象,每一个文件都被保存在一个JavaFileObject继承的类中
        Iterable<? extends JavaFileObject> compilationUnits = fileManager.getJavaFileObjectsFromStrings(
                Arrays.asList("E:\\spring\\dynamicJava\\src\\main\\java\\com\\yuan\\getTaskByIO\\Person.java",
                        "E:\\spring\\dynamicJava\\src\\main\\java\\com\\yuan\\getTaskByIO\\Student.java"));

        //4. 确定options命令行选项
        List<String> options = Arrays.asList("-d", "E:\\spring\\dynamicJava\\src\\main\\java\\com\\yuan\\getTaskByIO\\classes");

        //5. 获取编译任务
        JavaCompiler.CompilationTask task = compiler.getTask(null, fileManager, diagnosticCollector, options, null, compilationUnits);

        //6. 编译源程序
        Boolean success = task.call();
        fileManager.close();
        System.out.println(success ? "编译成功" : "编译失败");

        //7. 打印信息
        for (Diagnostic<?> diagnostic : diagnosticCollector.getDiagnostics()) {
            System.out.printf("Code: %s%n" + "Kind: %s%n" + "Position: %s%n" + "Start Position: %s%n"
                            + "End Position: %s%n" + "Source: %s%n" + "Message: %s%n", diagnostic.getCode(),
                    diagnostic.getKind(), diagnostic.getPosition(), diagnostic.getStartPosition(),
                    diagnostic.getEndPosition(), diagnostic.getSource(), diagnostic.getMessage(null));
        }
    }
}

结果如下:

可以发现,由于我们的每一个类的.java文件中是如下规定的:

所以会在classes目录下建立相应的子包,删除这一行package试试,结果正常了。

3.3 JavaCompoler.getTask()编译内存中的代码

JavaCompiler不仅可以编译硬盘上的Java文件,而且还可以编译内存中的Java代码,然后使用reflection来运行它们。我们可以编写一个MyJavaSimpleObject类,通过这个类可以输入Java源代码。

public class MySimpleJavaFileObject extends SimpleJavaFileObject {

    private String contents = null;
    private String className;

    public MySimpleJavaFileObject(String className, String contents) {
        super(URI.create("string:///" + className.replace('.', '/') + Kind.SOURCE.extension), Kind.SOURCE);
        this.className = className;
        this.contents = contents;
    }

    public CharSequence getCharContent(boolean ignoredEncodingErrors) throws IOException {
        return contents;
    }

    public String getClassName() {
        return className;
    }
}
public class Test {
    public static void main(String[] args) throws Exception {
        Test.compiler2();
    }
    public static void compiler2() throws IOException, IllegalAccessException, IllegalArgumentException,
            InvocationTargetException, NoSuchMethodException, SecurityException, ClassNotFoundException {
        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();

        DiagnosticCollector diagnostics = new DiagnosticCollector();

        //自己手写java代码
        String code = "public class HelloWorld{" +
                "public static void main(String[] args){" +
                "System.out.println(\"Hello World\");}" +
                "}";
        StandardJavaFileManager fileManager = compiler.getStandardFileManager(diagnostics, null, null);
        MySimpleJavaFileObject simpleJavaFileObject = new MySimpleJavaFileObject("HelloWorld", code);
        Iterable compilationUnits = Arrays.asList(simpleJavaFileObject);
        // options命令行选项
        Iterable<String> options = Arrays.asList("-d",
                "E:\\spring\\dynamicJava\\src\\main\\resources");// 指定的路径一定要存在,javac不会自己创建文件夹
        JavaCompiler.CompilationTask task = compiler.getTask(null, fileManager, diagnostics, options, null,
                compilationUnits);

        boolean success = task.call();
        System.out.println((success) ? "编译成功" : "编译失败");

        for (Object object : diagnostics.getDiagnostics()) {
            Diagnostic diagnostic = (Diagnostic) object;
            System.out.printf("Code: %s%n" + "Kind: %s%n" + "Position: %s%n" + "Start Position: %s%n"
                            + "End Position: %s%n" + "Source: %s%n" + "Message: %s%n", diagnostic.getCode(),
                    diagnostic.getKind(), diagnostic.getPosition(), diagnostic.getStartPosition(),
                    diagnostic.getEndPosition(), diagnostic.getSource(), diagnostic.getMessage(null));
        }

    }
}

最终结果如下:

3.4 总结

其实可以发现动态编译的基本流程如下:

public class CompileFileToFile{

  public static void main(String[] args) {
    //获取系统Java编译器
    JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
    //获取Java文件管理器
    StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);
    //定义要编译的源文件
    File file = new File("/path/to/file");
    //通过源文件获取到要编译的Java类源码迭代器,包括所有内部类,其中每个类都是一个 JavaFileObject,也被称为一个汇编单元
    Iterable<? extends JavaFileObject> compilationUnits = fileManager.getJavaFileObjects(file);
    //生成编译任务
    JavaCompiler.CompilationTask task = compiler.getTask(null, fileManager, null, null, null, compilationUnits);
    //执行编译任务
    task.call();
  }
}

其实由上面的执行流程可以发现,重要的就是task.call(),可以来跟踪一下task.call()的执行流程。

可以发现最终是由输出流写入到实际的磁盘class文件中的。

4. 输出到内存中而不是磁盘

从前面的分析我们看到,JavaFileObject 的 openOutputStream() 方法控制了编译后字节码的输出行为,也就意味着我们可以根据需要定制自己的 Java 文件对象。比如,当编译完源文件之后,我们不想将字节码输出到文件,而是留在内存中以便后续加载,那么我们可以实现自己的输出文件类 JavaFileObject。由于输出文件对象是从文件管理器的 getJavaFileForOutput() 方法获取的,所以我们还应该重写文件管理器的这一行为,综合起来的代码如下:

JavaFileManager jfm = new ForwardingJavaFileManager(fileManager) {
            public JavaFileObject getJavaFileForOutput(JavaFileManager.Location location,
                                                       String className,
                                                       JavaFileObject.Kind kind,
                                                       FileObject sibling) throws IOException {
                if(kind == JavaFileObject.Kind.CLASS) {
                    return new SimpleJavaFileObject(URI.create(className + ".class"), JavaFileObject.Kind.CLASS) {
                        public OutputStream openOutputStream() {
                            return new FilterOutputStream(new ByteArrayOutputStream()) {
                                public void close() throws IOException{
                                    out.close();
                                    ByteArrayOutputStream bos = (ByteArrayOutputStream) out;
                                    bytes.put(className, bos.toByteArray());
                                }
                            };
                        }
                    };
                }else{
                    return super.getJavaFileForOutput(location, className, kind, sibling);
                }
            }
        };

我们以前会默认将class文件存储到磁盘是由于以下两个原因:

  1. FileManger返回的时默认的JavaFileObject
  2. 默认的JavaFileObjectopenOutStream的方法如下:
@Override @DefinedBy(Api.COMPILER)
public OutputStream openOutputStream() throws IOException {
fileManager.updateLastUsedTime();
fileManager.flushCache(this);
ensureParentDirectoriesExist();
return Files.newOutputStream(path);
}
`ClassWriter`所用的输出流时NIO包的File类,会默认存储到我们设定的路径上。

写入到内存:

所以如果我们需要写入到内存,就需要修改JavaFileObjectopenOutStream()方法;同时这个JavaFileObject()是通过文件管理器的getJavaFileForOutput()方法获得的,默认的文件管理器获得的JavaFileObject中的输出方法就是输出到磁盘上,所以我们还需要一个实现一个自身的FileManager

5. 从内存到内存的动态编译+动态执行

5.1. 自定义的Java源码文件类

public class MySimpleJavaFileObject extends SimpleJavaFileObject {

    private String contents = null;
    private String className;

    public MySimpleJavaFileObject(String className, String contents) {
        super(URI.create("string:///" + className.replace('.', '/') + Kind.SOURCE.extension), Kind.SOURCE);
        this.className = className;
        this.contents = contents;
    }

    public CharSequence getCharContent(boolean ignoredEncodingErrors) throws IOException {
        return contents;
    }

    public String getClassName() {
        return className;
    }
}

5.2. 自定义的Java字节码文件类

这里需要重写openOutStream()方法,不输出字节码文件到文件,而是直接保存在一个输出流中。

public class MyJavaClassFileObject extends SimpleJavaFileObject {
    private final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();

    public MyJavaClassFileObject(String name, JavaFileObject.Kind kind) {
        super(URI.create("string:///" + name.replace('.', '/') + kind.extension), kind);
    }

    public byte[] getBytes() {
        return outputStream.toByteArray();
    }

    //编译时候会调用openOutputStream获取输出流,并写数据
    @Override
    public OutputStream openOutputStream() throws IOException {
        return outputStream;
    }
}

5.3 自定义的文件管理器

这里需要重写的方法时getJavaFileOutput()方法,输出我们自己写的Java字节码文件类

public class MyFileManager extends ForwardingJavaFileManager {

    private MyJavaClassFileObject javaClassObject;

    protected MyFileManager(StandardJavaFileManager standardJavaFileManager) {
        super(standardJavaFileManager);
    }

    public MyJavaClassFileObject getJavaClassObject(){
        return javaClassObject;
    }

    @Override
    public JavaFileObject getJavaFileForOutput(Location location, String className, JavaFileObject.Kind kind,FileObject sibling) {
        this.javaClassObject = new MyJavaClassFileObject(className, kind);
        return javaClassObject;
    }
}

5.4 自定义的类加载器

这里需要注意,默认的ClassLoader的defineClass()方法第一个参数接受的是全限定类名,classData是字节数组。

public class MyClassLoader extends ClassLoader {

    public Class loadClass(String fullName, MyJavaClassFileObject javaClassObject) {
        byte[] classData = javaClassObject.getBytes();
        return this.defineClass(fullName, classData, 0, classData.length);
    }
}

5.5 自定义的Java编译器

我们这里将编译器抽象出来。

public class DynamicCompiler {
    /**
     * 编译出类
     *
     * @param fullClassName 全路径的类名
     * @param javaCode      java代码
     * @return 目标类
     */
    public Class<?> compileToClass(String fullClassName, String javaCode) throws Exception {
        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
        DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<>();
        MyFileManager fileManager = new MyFileManager(compiler.getStandardFileManager(diagnostics, null, null));

        List<JavaFileObject> jfiles = new ArrayList<>();
        jfiles.add(new MySimpleJavaFileObject(fullClassName, javaCode));

        List<String> options = new ArrayList<>();
        options.add("-encoding");
        options.add("UTF-8");


        JavaCompiler.CompilationTask task = compiler.getTask(null, fileManager, diagnostics, options, null, jfiles);
        boolean success = task.call();
        if (success) {
            MyJavaClassFileObject javaClassObject = fileManager.getJavaClassObject();
            MyClassLoader dynamicClassLoader = new MyClassLoader();
            //加载至内存
            return dynamicClassLoader.loadClass(fullClassName, javaClassObject);
        } else {
            for (Diagnostic diagnostic : diagnostics.getDiagnostics()) {
                String error = compileError(diagnostic);
                throw new RuntimeException(error);
            }
            throw new RuntimeException("compile error");
        }
    }

    private String compileError(Diagnostic diagnostic) {
        StringBuilder res = new StringBuilder();
        res.append("LineNumber:[").append(diagnostic.getLineNumber()).append("]\n");
        res.append("ColumnNumber:[").append(diagnostic.getColumnNumber()).append("]\n");
        res.append("Message:[").append(diagnostic.getMessage(null)).append("]\n");
        return res.toString();
    }
}

步骤依旧和动态编译的时候一致,只不过在编译成功的时候,通过文件管理器获得我们自己写的class源码文件类,然后通过自定义的classLoader读取字节数组,实现创建一个类。

5.6 Main

public class Main {
    public static void main(String[] args) throws Exception {
        DynamicCompiler dynamicCompiler = new DynamicCompiler();
        String code = "package com.yuan.dynamic;\n" +
                "\n" +
                "/**\n" +
                " * Created by yuantb on 21/12/14.\n" +
                " */\n" +
                "public class Test {\n" +
                "    @Override\n" +
                "    public String toString() {\n" +
                "        return \"yuantb\"\n" + ";" +
                "    }\n" +
                "}\n";
        Class<?> clazz = dynamicCompiler.compileToClass("com.yuan.dynamic.Test", code);
        System.out.println(clazz.newInstance());
    }
}

结果:

  • 5
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: 在Java执行动态字符串,有几种方法可以使用。 一种方法是使用字符串连接符(`+`)。例如,假设你想要动态生成一个SQL语句,你可以使用字符串连接符来拼接字符串: ``` String sql = "SELECT * FROM users WHERE name='" + name + "' AND age=" + age; ``` 另一种方法是使用Java的`StringBuilder`类。这个类提供了一个可变的字符串对象,你可以使用`append()`方法来添加字符串: ``` StringBuilder sb = new StringBuilder(); sb.append("SELECT * FROM users WHERE name='").append(name).append("' AND age=").append(age); String sql = sb.toString(); ``` 使用`StringBuilder`类可能比使用字符串连接符快一点,因为它只需要一次内存分配,而不是在每次连接字符串时都分配一次内存。 还有一种方法是使用Java的`String.format()`方法,它可以使用占位符来格式化字符串: ``` String sql = String.format("SELECT * FROM users WHERE name='%s' AND age=%d", name, age); ``` 使用`String.format()`方法可能比其他方法更简洁,但是它可能比其他方法慢一点。 总的来说,选择使用哪种方法取决于你的具体情况。在性能要求较高的情况下,使用`StringBuilder`类可能是最好的选择。在简洁性和可读性更重要的情况下,使用`String.format() ### 回答2: 在Java执行动态字符串可以使用StringBuffer和StringBuilder这两个类。这两个类都是可变字符串,可以动态地添加、修改和删除字符串内容。 StringBuffer是线程安全的,适用于多线程环境下的字符串操作。在处理大量字符串拼接的情况下,可以选择使用StringBuffer,因为它的append方法是同步的,保证了线程安全。例如: ``` StringBuffer sb = new StringBuffer(); sb.append("Hello"); sb.append(" "); sb.append("World"); String result = sb.toString(); System.out.println(result); ``` 输出结果为:Hello World StringBuilder是线程不安全的,适用于单线程环境下的字符串操作。在单线程环境下,使用StringBuilder比StringBuffer效率要高。例如: ``` StringBuilder sb = new StringBuilder(); sb.append("Hello"); sb.append(" "); sb.append("World"); String result = sb.toString(); System.out.println(result); ``` 输出结果为:Hello World 在动态字符串的处理过程中,可以使用这两个类的append方法来动态地拼接字符串,也可以使用delete、insert等方法来删除、插入字符串内容,从而实现字符串动态操作。同时,这两个类还提供了一些其他的方法,例如reverse、replace等,方便对字符串进行进一步处理。 总之,通过使用StringBuffer和StringBuilder,Java中可以很方便地执行动态字符串的操作,满足业务需求。 ### 回答3: 在Java执行动态字符串,我们可以使用StringBuffer或StringBuilder类。 StringBuffer和StringBuilder类都属于可变字符序列,可以动态地修改字符串内容。 首先,我们可以使用StringBuffer类来处理动态字符串。它提供了许多方法来操作和修改字符串。可以通过append()方法向字符串末尾添加新的字符或字符串,可以通过insert()方法在指定位置插入新的字符或字符串,可以通过delete()方法删除指定位置的字符或字符串,还可以通过replace()方法替换指定位置的字符或字符串等。 另外,我们也可以使用StringBuilder类来操作动态字符串。StringBuilder类与StringBuffer类类似,提供了相似的方法来修改字符串内容。但与StringBuffer类不同的是,StringBuilder类是非线程安全的,因此在单线程情况下,使用StringBuilder的效率会稍高一些。 下面是使用StringBuffer执行动态字符串的示例代码: ``` StringBuffer sb = new StringBuffer(); // 创建一个StringBuffer对象 sb.append("Hello"); // 在字符串末尾添加新的字符串 sb.insert(5, " world"); // 在指定位置插入新的字符串 sb.delete(5, sb.length()); // 删除指定位置之后的字符 sb.replace(0, 5, "Hi"); // 替换指定位置的字符 System.out.println(sb.toString()); // 输出修改后的字符串 ``` 通过上述代码,我们可以实现对动态字符串执行和修改,并最终输出修改后的结果。 总结起来,利用StringBuffer或StringBuilder类,我们可以在Java中高效地执行动态字符串的操作。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值