这个String类是被final所修饰 因此它是不能被继承的
所有的类都是默认继承于object类
String类有两种形式
str是一个变量
注意:
person是变量 是引用类型
new Person();这个东西是在堆上
一种是通过String类创建一个变量把这个变量赋值一个字符串
new出来一个对象
通过引用来打印一个字符串
第三种
两种形式是真是假
这就要看String类在内存中的布局了
String的构造方法可以查看
哈哈哈哈感觉好干啊!!!!!
现在是2021年11月11日凌晨0点01分
好干啊!!!!!!!!!!!!!!!!!!
18年了我天啊!!!!!!!!我怎么还单身啊!!!!!!!
算了
我还是分析String内存存储吧 害
1.字符串常量池
从JDK1.7开始我们就把字符串常量池放到堆中去了
因此我们在堆中开辟一块空间叫做字符串常量池
字符串常量池只存放字符串常量
它不会存放除字符串之外的常量如:final int a;之类的
2.
之后由于hello是由双引号引起来的
所有hello是一个字符串常量 它存放于字符串常量池中
它是会占用内存的
因此str1指向这一块内存地址
3.
当str2再一次new一个对象之后
我们由于在字符串常量池中已经存了一个hello了
因此我们不把hello再一次存到字符串常量池中 只是会指向同一个hello
由于str2 new了一个对象
因此肯定在堆上开辟了一块空间
original表示是一个引用地址 表示str2创建出的hello的地址
又由于hello已经由str1创建过了
那么original的地址就是第一个hello处的地址
查阅可知:
我们构造方法中引用的普通的成员变量就是value
并且它的类型是一个数组
通过查阅可知 value是一个数组
这一块新空间肯定会有一个新地址
str2指向new String地址设为888
ok
其实我们总结一句话就是
一个hello在字符串常量池中我们只可以存储一份
由于str3并没有像str2一样new一个对象
因此我们在再一次String str3="hello";时就表示指向字符串常量池中同一位置的hello
总结:
1.一个相同的字符串常量只能在字符串常量池中开辟一次空间
如果往后还有相同的开辟时 我们都默认指向同一位置
2.我们在比较str1 str2 str3时都是比较它们的地址的
3.当你new一个对象之后中间地址会改变
再来一个
慢慢来
由于在编译的过程中常量字符串已经拼接完成了
因此我们可以知道
str1和str2是指向同一个堆上的字符串常量池上的hello的
ok 没问题的这个
因此str1==str2
接下来我们来看str3
这个str3的过程·:
一开始我们new了一个对象这个对象初始参数我们是定义为hel的
由于hel是不等于hello的因此我们在字符串常量池另外开辟一块空间来存hel
并且new的对象指向它
后来我们又加上了lo
中间这个+号起作用
它俩在堆上的空间进行拼接成hello
最终这个new出来的对象是指向这个拼接出来的hello’的
易知str1不等于str3
是否改变值?
切记不要以为传引用就可以改变值了 呵呵
str1="abc";只是代表它改变指向
指向了这个在堆上重新开辟的字符串abc
例子
分析一下:
先执行main
那么str1是局部变量存到栈上开辟空间
并且它是在堆上的字符串常量池开辟空间存放常量字符串hello的
因此str1执行地址设为888的hello
之后我们创建一个数组
这个数组在堆上开辟一块空间 存放'a'的
数组名val指向它
之后我们调用方法func
分析 :参数的传递过程
str1是实参传给形参
由于你传的是str1 str1原来指向地址为888的hello
因此str一开始也存放这个地址并且指向堆上字符串常量池上的hello
形参str在栈空间上开辟一块空间指向hello这个常量字符串
之后由于把这个str这个引用赋值为abcdef
因此指向改变 变为指向abcdef
但是hello并没有被改变
但是另外一个实参val传过去之后 传给数组array
形参数组名在栈上开辟空间 指向在堆上开辟的数组空间
array[0]='g';
这里是之间通过数组元素改变的
因此值改变
注意:
比较两个字符串的内容我们常用
str1.equals(str2)
一般我们比较两个字符串就是比较它俩的引用指向是否相同
注意2:
如果是一个简单类型 我们可以直接用"=="进行判断
然而当是一个字符串类型时 我们就不可以用==进行比较了
因为我们一般比较的是两个字符串的指向
因此我们常用 str1.equals(str2) 来比较两个字符串是否相等
记住一点:
equals前面的那一个字符串不可以是null
否则会发生空指针异常
但是后面的那个可以用null
intern方法:
str1先在字符串常量池中开辟一块空间存hello
然后是str2 先在堆上开辟一块空间存
在调用intern方法的时候我们先看在字符串常量池中是否与我们str2中要开辟的字符串hello同名的常量字符串
如果有
那么就返回常量池中这个hello的引用 即是赋值这个地址给str2
那么当使用intern方法的时候
我们并没有提前在常量池中有一个相同的hello
如图 str1在str2后面
分析:
如果没有intern方法 那么我们可以知道
先在字符串常量池中开辟一块空间存hello
在堆上new出来一块新空间之后 str2是要指向new String那一块空间的
这块空间是指向常量池中的hello的
但是这里有intern方法
这个就会导致 常量池中的hello地址被返回到str2中
即是str2被赋值了888这个地址
那么str2指向hello
之后我们又开辟了str1
同样指向第一个hello
因此结果为true
intern()方法又称手动入池
记住最好不要写下面这种代码:
因为它不可能在常量池中给你拼接
它只会生成许多临时的变量
因此不建议这种写法
字符串是不可以变化的 每一次都会生成一个新的对象
最终结果虽然可以
但是中间生成大量的临时变量
修改字符串:
表示从第一个位置即是从e开始提取出来
然后再拼接一个h
最后得到了字符串hello
反射可以直接改变Hello变成hello
拼接成字符串
情况1:
通过调用String的构造方法 来拼接字符串
情况2:
把val的字符数组在偏移量为1的情况下拼接三个字符
情况3:
提取处下标为1的字符 即是e
情况4:
把字符转换为字符数组
理解就行 没必要记住
利用
char ch=str2.charAt(i);提取i下标处的字符
看是否为一个只有数字的字符串?
何时用byte[ ]还是char[ ]?
equalsIgnoreCase忽略大小写的区别来比较两个两个字符串
比较两个字符串的长度
利用str1.compareTo(str2)来进行比较
由于前面的abc都相等的 后面str2多了def
返回-3
由于str1是小于str2的 小几个?小三个长度
因此返回-3
str1.compareTo(str2)
结果返回4
为什么?因为首字符e大于a 大4个ASCII码值
当这种e小于f时
str1.compareTo(str2) 返回-1
因为从第一个开始比较e比f小1
因此后面的就不会再去比较 直接返回-1
规则:
字符串截取:
在Java当中我们截取的时候默认是左闭右开的
因此这里(1,4)即是等价于[1,4)
那么从b开始截取到第二个b
注意截取的是三个而不是四个
1.trim
通过查找可知trim的返回值是String
因此要用String类来接收它
字符串查找
1.查找是否包含字串
2.
从头开始找abc如果找到了 那么就返回找到那个字符串abc的第一个字符的下标
即是2
3.从指定位置开始找
从下标为3的位置开始找
4.从后往前找
5.从指定位置开始 从后往前找
fromIndex表示从指定的位置去查找
研究以下两种算法:
6.
7.
表示从偏移量1处开始
举个例子:
第一个a的偏移量为0
第一个b的偏移量为1
字符串是否是以指定的字符串abc开头的
8.
判断是否由指定字符串结尾
看是否是以指定字符串bcde结尾
若是 返回true
若不是 返回false
注意:
重点来了
split类的返回值是一个String数组 类型
把这个字符串进行拆分
拆分的条件就是以空格为拆分条件
先分析一下这个foreach语句
它起到的作用是遍历这个数组
冒号的左边是这个数组的类型
右边是数组的数组名
limit表示限制了所分的组数
只分为两组
分割IP地址
对于这些符号处理时 我们都要加上"\\"
原因如下:
在想让机器知道你是什么符号的时候
必须要加上一个转义字符 \
但是呢?
这与c语言不一样
我们必须用两个'\'来表示这就是转义字符'\'
如果用一个'\'我们Java当中就不知道你这是转义字符'\'
因此我们用 \\ 来表示一个转义字符 \
因此我们得用"\\."来表示一个 .
但是\\时就有点恶心了
插一条转义字符的题:
"\0leo"
答案是0 而不是3
因为C语言中求字符串长度是遇见\0就停止
即\0是字符串的结束标志
并且记住一点在C语言算字符串长度时\0不算到长度中
如:"ABCD"的字符串长度为4
因为字符串是默认省略书写了\0 自带但不显示
这里其实就是"ABCD\0" 故结果为4
" abc\n\\\'\" "
长度为7 分析如下:
a b c \n \\ \' \''
后面四个都是转义字符 最后两个为 单引号和双引号
“abc\\\061xy”
长度为7
a b c \\ \061 x y
“abc\\\0xy”
长度为4
a b c \\
\0表示结束且不算里面
\\为转义字符 即是\
想以-和#同时分割
用 " |-|#"
但是注意第一个|之前要加上一个空格 不然就错了
这个了解就行 不常用
注意:
进行多次分割
先以&分割
再以=分割
笔试oj
import java.util.Scanner;//导包
public class Main {
public static String func(String str){
//把输入的字符串以空格为分割标识
String[] strings=str.split(" ");
String ret="";
//左边表示类型 s为定义的变量
//右边的表示数组名
for(String i:strings){
//数组被以空格拆开之后
//我们再进行拼接即可
ret+=i;
}
return ret;
}
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
//这里while表示多组输入
while (scan.hasNext()) {
//切记不可以用scan.next因为我们输入的字符串中间有空格
//用next遇见空格就直接结束了
String str = scan.nextLine();
String ret = func(str);
System.out.println(ret);
}
}
}
当while多组输入时
如何停止?ctrl+D
1.
toUpperCase与tolowerCase
都只是对字母有效
2.
这里的length() 表示方法
但是arr.length这里的length表示arr的属性
3.
4.
逆序一个字符串abcdef
public class Main {
public static String reserve(String str,int begin,int end){
//意思是把字符串中的每一个字符 放到数组中去
char[] arr=str.toCharArray();
while(begin<end){
char temp=arr[begin];
arr[begin]=arr[end];
arr[end]=temp;
begin++;
end--;
}
//不可以直接传回这个数组 因为返回类型是String
//因此我们要new一个String类型的
return new String(arr);
//返回这个的方法二
//return String.copyValueOf(arr);
}
public static void main(String[] args) {
String str="abcdef";
int begin=0;
int end=str.length()-1;
String ret=reserve(str,begin,end);
System.out.println(ret);
}
}
5.
算无良策 唯手熟尔
题中要把前面size个移到后面去
size是自己输入的
例如 size==3那么把前三个字符移到后面
如abcd 就变成 dabc
算法思想:
第一步:先把 前面要移动的size个字符逆序
第二步:再把size个字符 后面的字符进行逆序
第三步:再把整体逆序一遍
import java.util.Scanner;
public class Main {
public static String reserve(String str,int begin,int end){
//意思是把字符串中的每一个字符 放到数组中去
char[] arr=str.toCharArray();
while(begin<end){
char temp=arr[begin];
arr[begin]=arr[end];
arr[end]=temp;
begin++;
end--;
}
//不可以直接传回这个数组 因为返回类型是String
//因此我们要new一个String类型的
return new String(arr);
//返回这个的方法二
//return String.copyValueOf(arr);
}
public static String func(String str,int size){
//判断传入的size是否可取
if(str==null||size<0||size>str.length()){
return null;
}
str=reserve(str,0,size-1);
str=reserve(str,size,str.length()-1);
str=reserve(str,0,str.length()-1);
return new String(str);
}
public static void main(String[] args) {
Scanner scan=new Scanner(System.in);
while(scan.hasNext()){
String str=scan.nextLine();
int size=scan.nextInt();
String strings=func(str,size);
System.out.println(strings);
}
}
}
3.后两者有许多String不具备的方法
为什么StringBuilder StringBuffer不会产生大量的临时对象呢?
因为它是调用append append的返回值类型是this
最后进行一次toString()的原因就是为了把这个拼接之后的字符串赋值给ret
1.
通过反汇编我们可以知道
每一次循环都会new出来一个新对象 那么就会导致运行之后产生大量的对象
2.
总结:
1、重写实现的是运行时的多态,而重载实现的是编译时的多态。
2、重写的方法参数列表必须相同;而重载的方法参数列表必须不同。
3、重写的方法的返回值类型只能是父类类型或者父类类型的子类,而重载的方法对返回值类型没有要求。