Java 的基础知识回顾之字符串
一, 引言
很多人喜欢在前面加入赘述, 事实上去技术网站找相关的内容的一般都应当已经对相应知识有一定了解, 因此我不再过多赘述字符串到底是什么东西, 在官网中已经写得很明确了, 字符串实际上是一种特殊的类, 内置了一个字符数组 (所以你能对它进行下标查找, 包括集合的 arraylist 底层也是一个数组), 并附加了很多其他方法, 方便我们进行一些操作. 有兴趣的话可以去百度中查阅相关内容. 我事先说好了, 本文只是我用来回忆和复习的一个粗略笔记, 以及一些实验代码. 如果需要学习 Java, 建议去慕课网或者云课堂.
二, 关于 string 类型的一些常识
Java 的类基本上都继承于 Object 类, 也就是都继承了其中的 tostring 方法, 来标识其指向的地址, 这是因为 Java 对象名称实际上只是一个引用, 真正的东西还在堆中. Java 的理念来源于 C 艹, 但是字符串的运算符实际上不是重载而是在 jvm 中动了手脚, Java 中任何运算符都不能重载! 要细究这个问题就要探究 JVM, 我对 Java 虚拟机也是一知半解, 所以就不猪鼻子插葱了.
1.1Java 的相关入门基础
- package TheUnity5;
- //java 中的字符串连接通过运算符加号完成, 他方便了在字符串换行时候的连接
- public class Demo1 {
- public static void main(String[] args){
- String str="the java like skr rap";
- String str2="the java like"
- + "skr2 rap";//java 编译器不允许一个字符串跨越两行
- System.out.println(str);
- System.out.println(str2);// 一个显示效果
- System.out.println("--------------------");
- Boolean str3=Boolean.valueOf(false);
- System.out.println(""+str3);//string 连接数据类型
- Object str4=new Object();
- System.out.println(str4.toString());
- // 通过对照说明了封装类其实内部已经对 tostring 进行了重写
- }
- }
而事实上很多类的 tostring 也进行了重写, 格式化输出的实际上是类的 tostring 方法
三, 字符串的检索方法
Java 内置了字符串的检索方法 indexof, 这个方法经过了重载, 允许字符和字符串作为参数, 且能够指定检索的起始位置, 需要注意的是他会返回取到的第一个匹配值然后结束如果你需要检索所有内容你需要进行一个循环判断, 而这个方法当你检索的位置超出时, 会产生负值. 因此在循环判断的过程中, 需要注意很多因素. 当然你也可以一个一个用 charAt 去截取然后再去判断, 事实上从算法的时间复杂度来说, 这两个家伙都是遍历, 坏的很.
1.2Java 检索指定字符串的出现位置
- package TheUnity5;
- // 这里总结字符串的相关检索方法
- public class Demo2 {
- public static void main(String[] args){
- String str="do you like van♂ 游戏?";
- System.out.println(str);
- System.out.println("------------------");
- System.out.println("length:"+str.length());
- System.out.println(str.indexOf('o'));// 这里返回的是第一个 O 的位置.
- System.out.println(str.indexOf('o',str.length()-1));// 这里返回从 2 开始的第一个 o 的位置, 包括 2 也在检索范围内
- // 如果想要获得所有的指定字符的位置, 只能通过循环的方式
- talk1(str,'o');
- talk2(str,'o');
- }
- // 通过对每一个字符进行匹配确定位置
- public static void talk1(String str,char ch){
- int size=str.length();
- for(int i=0;i<size-1;i++){
- char a=str.charAt(i);
- if(a==ch){
- System.out.print(i+" ");
- }
- }
- System.out.println("talk1 结束");
- }
- // 实际上 indexof 底层也是遍历匹配, 但是对安全性做了很多措施
- public static void talk2(String str,char ch){
- int i=0;
- do{
- i=str.indexOf(ch,i);
- if(i==str.lastIndexOf(ch)){
- System.out.print(i+" ");
- break;
- }
- if(i>=0){
- System.out.print(i+" ");
- i++;// 注意这个位置
- }
- }while(i>=0);
- System.out.println("talk2 结束");
- }
- }
四, Java 的字符串修改操作
Java 实际上是开发者因为感受写一个机顶盒 c++ 程序非常烦躁下, 开发出的一款语言. 非常随意, 就和它的名字来源于当时喝的咖啡 (其实本来是另外一种咖啡但是名字已经被注册了 23333). 因此重复的字符数组裁剪工作被封装成了对应的方法, 这就很 nice 了, 常见的操作有字符串的分割, 截取, 替换, 去空格等, 下面是相关的代码实现和结果.(懒癌入髓, 不过这也能够起到让你们认真看代码的效果 [理直气壮. jpg] )
1.3Java 的字符串操作
- package TheUnity5;
- //Java 中内置了一些对字符串的相关操作. 方便对字符串进行修改
- public class Demo3 {
- public static void main(String[] args){
- // 初始字符串
- String str="deep♂ dark♂ fantasy";
- // 字符串截取方法 substring 会产生一个临时的字符串副本, 本质上不会改变原来的字符串
- String str1=str.substring(5);
- String str2=str.substring(5,11);// 不包含第二参数
- System.out.println(str);
- System.out.println(str1);
- System.out.println(str2);
- System.out.println("------------------------");
- // 去除首尾空格, Java 的 trim 方法不会去除字符串内部的非首尾空格
- String str3=str.trim();
- System.out.println(str3+""+str.length()+" "+str3.length());
- System.out.println("------------------------");
- // 字符串替换中 Java 内置了 replace 方法, 但不建议使用因为他会将原字符串所以相同部分全部替换
- String str4=str.replace('♂', ' ');
- System.out.println(str);
- System.out.println(str4);
- String str5=change(str,"♂"," ",0,str.length());
- String str6=change(str,"♂","",str.indexOf("♂")+1,str.length());
- System.out.println(str5);
- System.out.println(str6);
- System.out.println("------------------------");
- //java 同时也内置了一些字符串开头和结尾的验证, 但事实上你会发现大部分字符串的操作方法你都可以自己去重新定义
- System.out.println(str.startsWith("deep"));
- System.out.println(str.startsWith("deep",1));
- System.out.println(str.endsWith("fantasy"));
- System.out.println(str.endsWith("fantasy"));
- // 从输出结果来看, 空格也被包含进去了
- System.out.println("------------------------");
- //Java 也有字符串的大小写转换, 可以参考我们自定义的替换方法思考他是怎么做到的.
- String str7=str.toUpperCase();
- String str8=str.toLowerCase();
- System.out.println(str7);
- System.out.println(str8);
- System.out.println(str7.compareTo(str8));// 顺带提一下, 有兴趣可以自己百度
- System.out.println("------------------------");
- // 字符串分割
- String[] sp=str.split("♂");
- talk(sp);
- sp=str.split("♂", 2);// 后面表示分割为几份
- talk(sp);
- sp=str.split(" ");
- talk(sp);// 这样能去除所有空格, 注意第一个位置为空字符串而不是空.
- System.out.println("------------------------");
- }
- // 自定义字符串替换
- public static String change(String str,String demo,String demo2,int startindex,int endindex){
- String s1=str.substring(0, startindex);
- String s2=str.substring(startindex, endindex);
- String s3=str.substring(endindex);
- s2=s2.replace(demo, demo2);
- String s=s1+s2+s3;
- return s;
- }
- public static void talk(Object[] objs){
- for(int i=0;i<objs.length;i++){
- System.out.print("["+objs[i]+"]");
- }
- System.out.println();
- }
- }
五, 字符串的内存问题
如果你在高并发的环境下和我们的 demo 中一样写, 那么恭喜你, 你明天可以去人事部结一下工资了. 事实上原始的 string 不光效率不高, 而且上述方法都会产生一段字符串对象, Java 中 string 非常特殊, 如果你直接赋值给一个引用一段字符串, 那么它会先去常量区寻找, 如果找不到那么就会将该字符串注入常量池, 下次再引用的时候就会直接指向常量池中的内容. 而如果是通过 new 出一段字符串对象, 那么它会将你放进推中, 并且不会去理你, 除非我对这段对象进行操作.
1.4Java 字符串内存位置
- package TheUnity5;
- //Java 字符串的内存问题
- public class Demo4 {
- public static void main(String[] args){
- // 对于 String, 如果你对 Java 底层的代码有过了解会知道, 其实是一个封装的 char 数组
- // 而且, 是被 final 修饰的, 所以只能赋值一次, String 的内容实际是在常量池中的
- String str="神奇的沙德沃克先生";
- String s1="神奇的沙德沃克先生";
- System.out.println(str==s1);
- // 输出 true, 说明 str 与 s1 指向了同一内存地址
- // 如果不创建新的字符串对象, 相同内容的赋值实际上是多个引用指定一个内存 [证明常量池的存在]
- System.out.println("------------------------");
- String s2=new String("神奇的沙德沃克先生");
- System.out.println(s2==str);
- System.out.println(s2.equals(str));
- // 打印结果很明显是内容相同内存不同, 这里 string 对象存在堆中
- System.out.println("------------------------");
- String s3="神奇的沙德沃克先生";
- System.out.println(str==s3);
- System.out.println(s2==s3);
- // 仍然指向最早定义的字符串常量
- }
- }
1.5 对比试验
- package TheUnity5;
- public class Demo5 {
- public static void main(String[] args){
- // 这里接 demo4 继续讨论
- String str=new String("ABC");
- String s1="ABC";
- System.out.println(s1==str);
- // 输出结果为 false, 证明 new 一个 string 对象实际上避免了在常量池注入常量
- // 而事实上 String 的大部分方法都会返回一个新的 string 对象, 在程序开发中确实确保了安全性
- // 但事实上我们需要的不是堆中内存分配的过渡浪费.
- }
- }
重点来了, 你可能也听说过 jvm 的对象销毁机制, 它是由虚拟机自己决定什么时候销毁的, 这就会导致堆中对象不断累加, 常规开发环境没什么问题, 但是如果是高并发开发那么就会产生很大的内存开销, 这是不明智的. Java 的开发者也想到了这个问题, 所以提出了 stringbuffer, 提供线程安全的 string 增强类. 在后续开发中在非并发中又开发了 stringbuild 类, 当然后者是线程不安全的, 但是性能更高. 对这两种增强类的操作不会产生新的对象, 因此节省了堆的开销.
1.6 两种增强类
- package TheUnity5;
- // 这里开始正式讲 string 的两种拓展, 他们都是在原对象上进行修改, 达到节省空间的效果
- public class Demo6 {
- public static void main(String[] args){
- //Stringbuild
- System.out.println("---------------------");
- StringBuilder sb=new StringBuilder("NMSL");
- StringBuilder sb1=new StringBuilder("CNM");
- sb.append(sb1);// 在后部添加
- System.out.println(sb.toString());
- sb.insert(0, "脏鱼");// 指定位置插入
- System.out.println(sb.toString());
- sb.delete(0, 3);
- System.out.println(sb.toString());
- System.out.println("---------------------");
- StringBuffer sf=new StringBuffer("NMSL");
- sf.append("CNM");
- //stringbuffer 同上, 而且更安全.
- System.out.println(sf.toString());
- // 事实上是先有的 StringBuffer, 然后有的 StringBuild, 在多线程中前者安全后者不
- // 在单线程中后者比前者性能更好
- }
- }
六, 正则表达式
正则表达式实际上是一种规范, 在不同的语言会有些许不同, 而 Java 中采用类对其进行处理, 具体的语法可以参考各个技术论坛. PS: 我是真的烦背书 T_T, 如果开发要用到这个, 我可能得去翻相关的文档, 实在记不住.
1.7Java 正则表达式的两种用法
- package TheUnity5;
- import java.util.regex.Matcher;
- import java.util.regex.Pattern;//Java 正则表达式包
- // 严格意义来说正则表达式并不是一种语言, 而是一种规范, 不同语言在部分地方有细微查异
- // 本文只对 Java 的正则表达式讨论
- public class Demo7 {
- public static void main(String[] args){
- String content="风见啸大胜利!";
- String par=".* 胜利.*";
- boolean isMatch = Pattern.matches(par,content);
- System.out.println(isMatch);
- System.out.println("--------------------");
- String content2="15270003639";
- String par2="1[358]\\d{9}";
- boolean isMatch2=content2.matches(par2);
- System.out.println(isMatch2);
- // 以上是正则表达式的两种写法, 更多的内容推荐从菜鸟教程这些网站中学习
- }
- }
总结
漏洞百出, 懒惰至极, 建议当场出柜 [划掉] .
来源: https://www.cnblogs.com/Huangxin2015/p/11059831.html