大家好我是霜华,随着考试月的到来和开发工作的愈发繁忙,javaj基础篇的博客有时候经常断更,但无论怎么样我都会抽出时间把自己的知识全部展示出来, 毕竟我的背后是北风呐,那个高校中互联网人成长的土壤
其实,北风技术部的学生很多都是大一新生,这么多学生找我说他想要学技术其实我很开心,但同时我从他们身上也看到了我当年的影子,(也不是当年啦,就去年而已,只是这一年来自己实在成长太多,有种自己经历了很久的感觉)因为自己比大部分同龄人起步早些稍有骄纵,认为时间还有很多,也会经常懈怠。但其实很多同龄人已经比我们优秀很多,越优秀也更努力,天下之大,蓬勃汹涌的后浪们比比皆是,我们作为他们的其中一员,为什么不能成为冲在最前面的那朵浪花,我们年轻,我们有看似无限的美好年华,这段时间不就是拿来为自己的生活拼命的么,每天都在为自己的未来奔跑,
我们骄傲,自比天高,这有什么错,因为我们终将成为撑住天的栋梁!!
我们心气旺盛,不服一切,这有什么错,因为我们曾为夸下的每一句海口拼命着
或许年轻的我们还悟不到人生的真意,但有一天站在顶峰或许能看懂更多。
朋友啊,爬过一座座山峰,去看看外面的世界吧!!!
做那个独一无二的后浪!!!!
做完这期是5月31号是五月天的线上演唱会,某一年的夏天的5月于我而言有太多的故事,这时候听听那首突然好想你想想那个想见却永远见不到的人吧。
抱歉最近在调整自己的学习状态,同时也很忙,写每一次博客我都要找大量的资料,进行整和
这次的String很多知识点也要我研究完 JVM才能继续说
尽量在保证质量的情况下3天一更
工具类:
1.构造方法私有 ,没有公有的构造方法,不能创建对象
2.属性和方法都是static修饰,作为类资源,可以通过类名直接访问
常用方法集:
1.属于java.util包
2.通常不会用构造方法构造对象
方法集:
String uuid=UUID.randomUUID;//随机产生一组数字(绝对不重,开发中常常用来做数据库的索引ID)
开发实战运用:
String UUID =java.util.UUID.randomUUID().toString.replaceAll("-","");
return UUID;//UUID为32位,2的32次方的范围
1.属于lang 包
2.通过一个带输入流的构造方法创造对象
方法集
nextInt//int
nextFloat//float
next //返回String
nextLine//String
//实际使用
Scanner input=new Scanner(System.in);
int x= input.nextInt();
System.out.println(x);
Scanner input=new Scanner(System.in);
String x= input.next();
String y= input.next();
System.out.println(x);
System.out.println(y);
//next方法读的是空格,以空格作为结尾,开始调用下一个next方法//其他是以换行符作为结尾开始下一个方法
1.java.lang包
2.三个属性out in err
3方法
GC()//提醒垃圾回收器回收内存
Exit()
1.java.util//
2.⽆static调用属性方法 需要new对象
方法级
Date d = new Date (154786767092L)//构建之前的时间//加个L防⽌报错,F的话是double存进float Date date =new Date():
System.out.println(date);输出格林威治时间格式
DateFormat df =new SimpleDateFormat(“yyyy-MM-dd KK:mm:ss”)//在括号⾥⾯给个格式String value =df.formate(date);//改时间格式
通常调⽤⽆参数构造⽅法创建对象也可以调⽤带long参数Date d =new Date(154786767092L)Date date =new Date();
boolean b =date.before(d);//date是否在在传递参数(d)之前boolean b =date.before(d)//date是否在在传递参数(d)之后
setTime(long time)
long time = getTime() boolean = date1.after(date2)
-boolean = date1.before(date2)
-int = compareTo() 调⽤⽅法的对象实现接⼝Comparable int v = date.compareTo(d)date靠后
-SimpleDateFormat
1.java.text
2.通过带String 参数的构造⽅法创建对象
String pattern = “yyyy-MM-dd HH:mm:ss” 3.String = format(date)//date是传递的参数类型
//抽象类
Calendar c =new Calendar();//如果前后两都报错证明没导包,
如果后⾯红线:1没有⽆参数构造⽅法,2.⽆参数构造⽅法私有3.当前类是抽象类或者是接⼝
//SingTon
//getInstance();指的是创键对象的意思所以创建这个对象⽤
Calendar c=Calendar.getInstance();
输出 c 会有⼀堆码表示很多时间信息
c.setTime(YEAR,2015);// 更改局部信息
date c.getTime//
int year=c.get();获取某⼀时间年份⽉份0-11 1.java.util
2.不能构建对象 getInstance(); 3.getTime setTime
before after get set getTimeInMillis()
TimeZone l另⼀个类
TimeZone t= TimeZone.getDefault();//获取对象TimeZone tz =c.getTimeZone();
输出(tz.getID)//时区ID
输出(tz.getDisplayName())//中国标准时间
TimeZone = TimeZone.getDefault();
TimeZone = c.getTimeZone(); getID()
getDisplayName();
1.String//最常⽤的类 StringBuffer StringBuilder
2.java.lang 类 字⾯值// 两种赋初值的⽅法//对象创建
String s1 =new string (“abr”)
.String的构造⽅法有15个
下面列举常用的
String s=“abc”//可以⽤常量的形式赋值"abc"//只是看做常量 ,这是⼀个String类型的对象放在常量区中
private final char value[];//这是String中实际存数据的地方,是一个char的数组
private int hash;//String对象的hash值//相当一个对象的ID
//1-------------------------
new String("abc");
public String(String original) {
this.value = original.value;
this.hash = original.hash;
}//带String的构造方法
//2--------------------
new String(char[])
public String(char value[]) {
this.value = Arrays.copyOf(value, value.length);
//调用Arrays自带的复制数组的方法,将原资源的数组在内存中copy一份新数组,将这个新数组的地址引用指向我们的value属性
}
//Arrays的copyof源码:
public static char[] copyOf(char[] original, int newLength) {
char[] copy = new char[newLength];//建立一个新数组作为return
System.arraycopy(original, 0, copy, 0,
Math.min(original.length, newLength));
return copy;
}//其实看过ArrayList源码的同学对这个方法并不陌生,ArrayList底层数组的扩容就是用Arrays
//3------------------------------------
new String(StringBuffer)//三者之间可以互相构建new String(StringBuilder)//
public String(StringBuffer buffer) {//StringBuffer是一个变种版的String
synchronized(buffer) {
this.value = Arrays.copyOf(buffer.getValue(), buffer.length());
}
}
//-----------------
new String(byte[],0,i)//从数组0到i变成String new String(byte[],charset)//按照什么样⼦来组合new String(byte[])//
是一个非常特殊的引用数据类型 可以像基本数据类型一样创建赋值
**1.String特点—不可变特性:
什么叫不可变特性:地址指向的内存中的String对象,长度不可变,元素不可变
什么导致不可变:
源码:
private final char value[];
//内部实际的存储数组是final修饰的,final修饰的变量,不可更改。
//private 修饰,内部如果没有提供方法修改,不允许直接通过外部类访问甚至是进行更改
设置为不可变有什么好处:
1.提高效率和安全性,copy对象的内容是不复制内容本身而是复制一个地址;在多线程同时操作数据的情况下,数据不会改变,能保证线程安全。当然技术是把双刃剑,这确实在一定程度上,降低了程序的灵活性
2.字符串常量池的需要:当创建一个String对象时,字符串串值会存在与常量池中,如果在创建一个相同的对象引用直接指向常量池的内容(创建一个对象真的很耗费资源,在我正常开发中我一直都尽可能少的创建对象)
如String s1 =“abcd”
String s2=“abcd”
//两者其实是指向同一个内容,如何判断用hash码就能看到
看过我之前文章的人会发现,啊!之前不是说hashcode 的结果是:对象名@hash码 的形势么
遇事不决,源码解决:
//String内部的hashCode方法,很明显重写了Object方法
public int hashCode() {
int h = hash;
if (h == 0 && value.length > 0) {
char val[] = value;
for (int i = 0; i < value.length; i++) {
h = 31 * h + val[i];
}
hash = h;
}
return h;
}
String类中的常⽤⽅法 20+
(重写⽅法)
String 类型同时重写了equals方法//重写,子类修饰符权限大于等于父类,返回值类型小于等于父类,名字与参数列表必须与父类一致 抛出异常个数和类型都小于父类
重写后的源码:
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}//如果两个的地址引用相等,就是指向同一个对象,直接返回true
if (anObject instanceof String) {//instanceof 是用于判断对象的类型 如果anObject 是String 对象或者是String 的子类返回true
String anotherString = (String)anObject;
int n = value.length;
if (n == anotherString.value.length) {//1.先看看两个字符串长度是否相等,相等我们再进行下一步
char v1[] = value;//将String的value属性,也就是String内部的char数组赋值给v1
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {//从头开始遍历一旦有错立刻返回flase
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}
String 中 == equals( )的区别
== 比较对象底子
equals()比较两个String字符串的值
private int hash; // Default to 0//
public int hashCode() {
int h = hash;
if (h == 0 && value.length > 0) {
char val[] = value;
for (int i = 0; i < value.length; i++) {
h = 31 * h + val[i];
}
hash = h;
}
return h;
}
public int compareTo(String anotherString) {
int len1 = value.length;
int len2 = anotherString.value.length;
int lim = Math.min(len1, len2);//找出两个长度的最小值
char v1[] = value;
char v2[] = anotherString.value;
int k = 0;
while (k < lim) {
char c1 = v1[k];
char c2 = v2[k];
if (c1 != c2) {
return c1 - c2;
}
k++;
}
return len1 - len2;
}
所以返回值为0表示长度和字符都相等,返回值为负数或者正数不等,正数表示,前面的数都相等,只是另一个多出来了些值
相比于equals 它表示的信息稍微丰富些。
toString
//返回String对象的字面值
toString源码
public String toString() {
return this;
}
(开发中常⽤)
1、char = charAt(int index)//返回的是对应位置的字
2、int = codePointAt(int index)
//返回的是对应位置的字的code码前两个拥有加密,
for(int i=0;i
code-=48 //密钥result+=(char)code;
}
3、String = concat("");//拼接,//需要接受 //concat是在堆内存⾥new对象,所以空间⼤,string
容器接收返回值String不可变特性()
例⼦:
String s =“abcdegf”;
s= s.concat(“g”)//空间相对较⼤
//因为String的不可变特性,s.concat 其实是底层做了一个char数组的处理,然后根据这个新char数组返回了一个新的堆内存对象
//所以其实我们需要将地址引用重新指向他
public String concat(String str) {
int otherLen = str.length();
if (otherLen == 0) {
return this;
}
int len = value.length;
char buf[] = Arrays.copyOf(value, len + otherLen);
str.getChars(buf, len);
return new String(buf, true);
}
//我们感受一下这个不可变特性
public class TextMain {
public static void main(String []args){
String a="1";
String b=a.concat("2");
System.out.println("a的输出为"+a);
System.out.println("b的输出为"+b);
}
}
4、boolean = contains("")//是否包含//但是字符必须连着
5、boolean = endsWith("")//是否以什么开头//扫描⽂件
6、boolean = startsWith("")//
7、byte[] = getBytes();//不传参数的话以平台(操作系统)默认的字符集拆分
8、byte[] = getBytes(“charset”);//按照给定的来拆//
9、char[] = toCharArray();//把字符串拆成数组
10、int = indexOf();//(‘a’)返回a第⼀次所在的位置(‘a’2)从第三个数,(97)找code是97的
11、int = lastIndexOf();//从后往前找第⼀个位置;
12、length();//当前字符串⻓度
13、*boolean = matches(String regex) regular expression
14、replace(‘old’,‘new’);//交换,需要接受新的
15.、replaceAll
16、replaceFirst//换第⼀个
*15、 String[] = split("",2)//拆分按照字符⾥的东⻄拆,2的意思是我就拆两段,后⾯这个2是重
载
16、String = substring(int beginIndex,int endIndex)//截取字符串,[,)
17、String = substring(int beginIndex)//从第⼏个截取
18、trim();//把旁边的空格去掉//需要返回第三个梯队(不是很常⽤)
19、toLowerCase()//字符串全变成⼩写字⺟
20、toUpperCase();
21、String.valueOf();//将所有基本类型转化成String//String x =String.valueOf(10);/因为是静态⽅法,将基本类型转化为String类型
22、s1.equalsIgnoreCase(s2)忽略⼤⼩写的⽐较,
23、compareToIgnoreCase
与String 三者的继承结构
//图片来源:https://blog.csdn.net/ifwinds/article/details/80849184
区别:
与String底层依然是一个字符数组只是没有final修饰
char[] value;
String是不可变字符, StringBuiler 与StringBuffer是可变字符串
StringBuiler 非线程安全 StringBuffer线程安全
1.属于java.lang
2.继承 实现
默认继承Object
实现接⼝Serializable, Appendable, CharSequence
3.构造⽅法
构造⽅法4个
private char[] value; 动态扩容 16⻓度//没有final
new StringBuilder(); 16
new StringBuilder(20); 20
new StringBuilder(“abc”); 3+16,来回互相构建吗
new StringBuilder(charSequence);
3.常⽤的⽅法
⾃⼰类中独有的⽅法 String类没有的
2.String创建对象的特点
String s = “a”+“b”+“c”+“d”//中间产⽣7个对象a b ab c abc d abcd
String是一个不可变类,既然不可变,如果想改变字符串的信息做字符串的拼接该怎么做呢
1.String 的 + 号 拼接字符串
String a ="我是"
String end =a+“霜华”
注意:+号还是我们理解的那个作为运算符的+号,它不是其他语言的运算符重载,java本身也并不支持运算符重载,这是一个java的语法糖
运算符重载:在计算机程序设计中,运算符重载(英语:operator overloading)是多态的一种。运算符重载,就是对已有的运算符重新进行定义,赋予其另一种功能,以适应不同的数据类型
语法糖:语法糖(Syntactic sugar),也译为糖衣语法,是由英国计算机科学家彼得·兰丁发明的一个术语,指计算机语言中添加的某种语法,这种语法对语言的功能没有影响,但是更方便程序员使用。语法糖让程序更加简洁,有更高的可读性。
1.+号实现的底层原理:
a+“霜华”:
底层其实是jvm通过+号隐性的调用了StringBuiler:这里我运用了反编译工具去处理源代码:
计算机软件反向工程(Reverse engineering)也称为计算机软件还原工程,是指通过对他人软件的目标程序(比如可执行程序)进行“逆向分析、研究”工作,以推导出他人的软件产品所使用的思路、原理、结构、算法、处理过程、运行方法等设计要素,某些特定情况下可能推导出源代码。反编译作为自己开发软件时的参考,或者直接用于自己的软件产品中。
String end =new StringBuiler().append(a).append("杨龙").toString();
字符串常量+ 拼接实现原理是使用StringBuiler.append
2,String 本身的concat 方法
public String concat(String str) {
int otherLen = str.length();
if (otherLen == 0) {
return this;
}
int len = value.length;
char buf[] = Arrays.copyOf(value, len + otherLen);
str.getChars(buf, len);
return new String(buf, true);
}
创建一个新数组,长度是旧老字符串长度之和,再copyof数组再调用关于char[]的构造方法
3.StringBuffer 与StringBuiler 的appen方法:
//底层机制都是一样的
之前提到Stringbuffer是线程安全为什么线程安排是因为synchronized
public synchronized StringBuffer append(String str) {
toStringCache = null;
super.append(str);//
return this;
}
一个synchronized 修饰的 线程锁,当一个线程使用了对象方法后,不允许其他线程访问这个对象
//大家都知道super是调用父类的子类覆盖掉的方法:我们来看看StringBuffer和StringBuiler共同继承的AbstractStringBuilder类的append方法:
直接拷贝字符到内部的字符数组中
public AbstractStringBuilder append(String str) {
if (str == null)
return appendNull();
int len = str.length();
ensureCapacityInternal(count + len);//确定一下新数组是否够位置,如果不够,扩容。有ArrayList那味了
str.getChars(0, len, value, count);
count += len;
return this;
}
4.StringUtil.jion
除了java内置的字符串 还有一些开源库提供的一些工具(开源库:可以了解下关于开源的一些历史,个人理解就是一些非语言的创始开发者,提供的一些外部工具)如apache.comons提供的StringUtils类
String resource1="I am";
String resource2="杨龙";
String resource=(StringUtils.join(resource1,resource2));
//StringUtil 还有一个功能是:将数组和集合通过某些符号连接到一起形成新的字符串
String [] resource ={"i am","杨龙"}
String result =StringUtils.join(resourcee,",");
StringUtil.jion 实现原理:
public static String join(final double[] array, final char separator, final int startIndex, final int endIndex) {
if (array == null) {
return null;
}
final int noOfItems = endIndex - startIndex;
if (noOfItems <= 0) {
return EMPTY;
}
//对数组做一些长度判断
final StringBuilder buf = new StringBuilder(noOfItems * 16);//依然还是用StringBuilder进行字符串的拼接
for (int i = startIndex; i < endIndex; i++) {
if (i > startIndex) {
buf.append(separator);
}
buf.append(array[i]);
}
return buf.toString();
}
+号如果是出现在循环体中,每一次执行都需要创建一次StringBuiler对象,一是效率太低二是因为频繁的创建对象,会对内存造成极大的内存资源浪费。
StringBuiler 就是为了定义可变字符串 和字符串可变操作的
不过在非循环中,+ 其实和StringBuilder是差不多的,最好 用+号
/—
十、字符串常量池
这块也很重要,有了这个次啊能理解接下来的笔试题
java内存分配中 总共有Class常量池、运行时常量池、字符串常量池、三种常量池
字符串的分配毕竟也是对象,内存需要需要耗费很多的空间时间,jvm为了提高性能创建字符串时候会进行一些优化:使用字符串常量池、每当创建字符串常量时候jvm会检查一遍常量池,如果有相同的东西,就把这个实例的地址以用指向它,因为是不可变嘛,如果你想操作变字符串就需要所以常量池一定不存在两个相同的,
笔试题:
String s1=”abc”;
String s2=”abc”;
String s3=new String(“abc”);
String s4=new String (“abc”);
System.out.println(s1s2)//true
System.out.println(s1s3)//false
System.out.println(s3==s4)//false
System.out.println(s1.equals(s2))//true
System.out.println(s1.equals(s3))//true
System.out.println(s3.equals(s4))//true
/—
s1 和s2 指向的都是常量区的abc
s3 和 s4 是单独创建一个对象在堆内存开辟了一个内存空间
final与+号字符串
final修饰的变量,不可变,它是在编译时,产生的变量,存在静态元素区里面,这个知识对解下面这道题很重要
final String a="a";//final是编译时就就定义了的变量
String b="b";//在运行时在堆内存定义了
String ab=a+b;//+ 看过源码都知道是StringBuffer append后toString 返回了一个新的对象
String ab1="ab";
System.out.println(ab==ab1);//false
String a="a"; //这里就和上文一样了运行时在 常量区分别创建了a和b a+b,StringBuffer 在堆内存返回了一个对象//而ab1指向的”ab“是在静态常量区,地址不一样
String b="b";
String ab=a+b;
String ab1="ab";
System.out.println(ab==ab1);//false
final String a="a";//final是编译时就设置的只
final String b="b";
String ab=a+b;//ab两个变量都是编译
String ab1="ab";
System.out.println(ab==ab1);//true
十、String字符串的长读限制
在String的构造方法中,有支持用户传入length来执行长度限定的重载方法
public String(byte bytes[], int offset, int length)
长度是int类型,理论上长度应该是int 的最大范围值 2^31-1//减的是0
String s =”111111......1111“十万个1
执行javac时,会出现异常,提示常量字符串过长
但是要知道像这样String=”xxxx“ 是存在常量池里面的, 常量池也有它自己的相关的一些规定
关于常量池定义
CONSTANT_String_info 用于表示 java.long.String 类型的常量对象
CONSTANT_String_info{
u1 tag;
u2 string_index;//它的值必须是对常量池的有效索引,常量池在索引处项必须是CONSTANT_Utf8_info 结构 表示一组Unicode 马点序列,最终初始化形成一个String对象
}
CONSTANT_Utf8_info {
u1 tag;
u2 length;
u1 bytes[length];//指明bytes数组 u2是一个表示两字节的无符号数 ,两字节就是16个bit位 可表示的最大值为2^16-1=65535
}
so:常量池规定他内部的字符串长度不能超过65535(小于65535不能等于)
//但上面只是说在编译期常量区的规定
如果是运行期在堆内存区的化就是正常的int指代的2^32-1 的范围
如何让字符串是在运行期的堆内存中
答案如下:
s+“1”
参考
为什么阿里巴巴不建议在for循环中使用"+"进行字符串拼接
https://mp.weixin.qq.com/s/fLUf8V0Qahe8piNrRp1UVQ