常用类
1、Object类
Object是所有类的父类,任何类都默认继承Object
由于所有的类都继承在Object类,因此省略了extends Object关键字
该类中主要有以下方法:
-
toString()
-
getClass()
-
equals()
-
clone()
-
finalize(
其中toString(),getClass(),equals是其中最重要的方法。
注意:Object类中的getClass(),notify(),notifyAll(),wait()等方法被定义为final类型,因此不能重写。
1.1、clone()方法
protected native Object clone() throws CloneNotSupportedException;
clone顾名思义就是复制, clone方法被对象调用,所以会复制对象。
创建对象的方式
-
使用new操作符创建一个对象
-
使用clone方法复制一个对象
相同和不同
new操作符的本意是分配内存。程序执行到new操作符时, 首先去看new操作符后面的类型,因为知道了类型,才能知道要分配多大的内存空间。分配完内存之后,再调用构造函数,填充对象的各个域,这一步叫做对象的初始化,构造方法返回后,一个对象创建完毕,可以把他的引用(地址)发布到外部,在外部就可以使用这个引用操纵这个对象。
clone在第一步是和new相似的, 都是分配内存,调用clone方法时,分配的内存和源对象(即调用clone方法的对象)相同,然后再使用原对象中对应的各个域,填充新对象的域, 填充完成之后,clone方法返回,一个新的相同的对象被创建,同样可以把这个新对象的引用发布到外部。
复制对象VS复制引用
Person p = new Person(); Person p1 = p;
Person p = new Person(); Person p1 = p.clone();
深拷贝VS浅拷贝
对象有String类型的属性name是,其拷贝方式有两种:
- 将原对象name的引用值拷贝
- 根据原对象name指向的字符串对象创建一个相同的字符串,获得它的引用值给新name
clone方法执行的是浅拷贝
如果想要深拷贝一个对象, 这个对象必须要实现Cloneable接口,实现clone方法,并且在clone方法内部,把该对象引用的其他对象也要clone一份 , 这就要求这个被引用的对象必须也要实现Cloneable接口并且实现clone方法。
1.2、toString()方法
public String toString() { return getClass().getName() + "@" + Integer.toHexString(hashCode()); }
Object 类的 toString 方法返回一个字符串,该字符串由类名(对象是该类的一个实例)、标记符“@”和此对象哈希码的无符号十六进制表示组成。
1.3、getClass()方法
public final native Class<?> getClass();
返回次Object的运行时类类型
不可重写,调用时,一般和getName()联合使用,getClass().getName();
1.4、finalize()方法
protected void finalize() throws Throwable { }
该方法用于释放资源。因为无法确定该方法什么时候被调用,很少使用。
Java允许在类中定义一个名为finalize()的方法。它的工作原理是:一旦垃圾回收器准备好释放对象占用的存储空间,将首先调用其finalize()方法。并且在下一次垃圾回收动作发生时,才会真正回收对象占用的内存。
垃圾回收:
-
对象可能不被垃圾回收。只要程序没有濒临存储空间用完的那一刻,对象占用的空间就总也得不到释放。
-
垃圾回收并不等于“析构”。
【析构函数(destructor) 与构造函数相反,当对象结束其生命周期,如对象所在的函数已调用完毕时,系统自动执行析构函数。析构函数往往用来做“清理善后” 的工作(例如在建立对象时用new开辟了一片内存空间,delete会自动调用析构函数后释放内存)。】
- 垃圾回收只与内存有关。使用垃圾回收的唯一原因是为了回收程序不再使用的内存。
finalize()的用途:
无论对象是如何创建的,垃圾回收器都会负责释放对象占据的所有内存。
这就将对finalize()的需求限制到一种特殊情况,即通过某种创建对象方式以外的方式为对象分配了存储空间。不过这种情况一般发生在使用“本地方法”的情况下,本地方法是一种在Java中调用非Java代码的方式。
1.5、equals()方法
public boolean equals(Object obj) { return (this == obj); }
Object中的equals方法是直接判断this和obj本身的值是否相等,即用来判断调用equals的对象和形参obj所引用的对象是否是同一对象
注意:即便是内容完全相等的两块不同的内存对象,也返回false
1.6、hashCode()方法
public native int hashCode();
返回该对象的哈希码值。
该方法用于哈希查找,可以减少在查找中使用equals的次数,重写了equals方法一般都要重写hashCode方法。这个方法在一些具有哈希功能的Collection中用到。
一般必须满足obj1.equals(obj2)==true。可以推出obj1.hashCode() == obj2.hashCode(),但是hashCode相等不一定就满足equals。不过为了提高效率,应该尽量使上面两个条件接近等价。
1.7、wait()方法
public final void wait() throws InterruptedException { wait(0); } public final native void wait(long timeout) throws InterruptedException; public final void wait(long timeout, int nanos) throws InterruptedException { if (timeout < 0) { throw new IllegalArgumentException("timeout value is negative"); } if (nanos < 0 || nanos > 999999) { throw new IllegalArgumentException( "nanosecond timeout value out of range"); } if (nanos > 0) { timeout++; }wait(timeout); }
方法的异常:
wait方法就是使当前线程等待该对象的锁,当前线程必须是该对象的拥有者,也就是具有该对象的锁。wait()方法一直等待,直到获得锁或者被中断。wait(long timeout)设定一个超时间隔,
如果在规定时间内没有获得锁就返回。
调用该方法后当前线程进入睡眠状态,直到以下事件发生。
-
其他线程调用了该对象的notify方法。
-
其他线程调用了该对象的notifyAll方法。
-
其他线程调用了interrupt中断该线程。
-
时间间隔到了。
此时该线程就可以被调度了,如果是被中断的话就抛出一个InterruptedException异常。
1.8、notify()方法
public final native void notify();
该方法唤醒在该对象上等待的某个线程。
public final native void notifyAll();
该方法唤醒在该对象上等待的所有线程。
2、包装类
虽然 Java 语言是典型的面向对象编程语言,但其中的八种基本数据类型并不支持面向对象编程,基本类型的数据不具备“对象”的特性——不携带属性、没有方法可调用。
这种借助于非面向对象技术的做法有时也会带来不便,比如引用类型数据均继承了 Object 类的特性,要转换为 String 类型(经常有这种需要)时只要简单调用 Object 类中定义的toString()即可
而基本数据类型转换为 String 类型则要麻烦得多。为解决此类问题 ,Java为每种基本数据类型分别设计了对应的类,称之为包装类(Wrapper Classes),也有教材称为外覆类或数据类型类。
基本数据类型 | 对应的包装类 |
---|---|
byte | Byte |
short | Short |
int | Integer |
long | Long |
char | Character |
float | Float |
double | Double |
boolean | Boolean |
每个包装类的对象可以封装一个相应的基本类型的数据,并提供了其它一些有用的方法。包装类对象一经创建,其内容(所封装的基本类型数据值)不可改变
基本类型和对应的包装类可以相互装换:
-
由基本类型向对应的包装类转换称为装箱,例如把 int 包装成 Integer 类的对象;
-
包装类向对应的基本类型转换称为拆箱,例如把 Integer 类的对象重新简化为 int。
2.1、包装类的应用
【1、实现int和Integer的相互转化】
可以通过 Integer 类的构造方法将 int 装箱,通过 Integer 类的 intValue 方法将 Integer 拆箱。
public static void main(String[] args) { int m = 500; Integer obj = new Integer(m);// 手动装箱 int n = obj.intValue(); // 手动拆箱 }
【2、将字符串转化为整数】
Integer 类有一个静态的 paseInt() 方法,可以将字符串转换为整数
parseInt(String s,int radix);
s 为要转换的字符串,radix 为进制,可选,默认为十进制
public static void main(String[] args) { String[] str = {"123", "123abc", "abc123", "abcxyz"}; for(String str1 : str){ try{ int m = Integer.parseInt(str1, 10); System.out.println(str1 + " 可以转换为整数 " + m); }catch(Exception e){ System.out.println(str1 + " 无法转换为整数"); } } }
【3、将整数转化为字符串】
Integer 类有一个静态的 toString() 方法,可以将整数转换为字符串。或者直接在整数后面加空字符串即可!
public static void main(String[] args) { int m = 500; String s = Integer.toString(m); String s2 = m+""; }
2.2、自动拆箱和装箱
public static void main(String[] args) { int m = 500; Integer obj = m; // 自动装箱 int n = obj; // 自动拆箱 }
自动装箱与拆箱的功能事实上是编译器来帮您的忙,编译器在编译时期依您所编写的语法,决定是否进行装箱或拆箱动作
虽然使用这个功能很方便,但在程序运行阶段您得了解Java的语义。例如下面的程序是可以通过编译的:
Integer i = null; int j = i;
这样的语法在编译时期是合法的,但是在运行时期会有错误,因为这种写法相当于:
Integer i = null; int j = i.intValue();
null表示i 没有参考至任何的对象实体,它可以合法地指定给对象参考名称。由于实际上i并没有参考至任何的对象,所以也就不可能操作intValue()方法,这样上面的写法在运行时会出现NullPointerException错误。
自动拆箱装箱是常用的一个功能,需要重点掌握。
一般地,当需要使用数字的时候,我们通常使用内置数据类型,如:byte、int、long、double 等。然而,在实际开发过程中,我们经常会遇到需要使用对象,而不是内置数据类型的情形。为了解决这个问题,Java 语言为每一个内置数据类型提供了对应的包装类。
所有的包装类(Integer、Long、Byte、Double、Float、Short)都是抽象类Number 的子类。
3、Math类
public final class Math{ //数学方法 }
Math 的方法都被定义为 static 形式,通过 Math 类可以在主函数中直接调用。
【常用值与函数】
属性/方法 | 描述 |
---|---|
Math.PI | 记录的圆周率 |
Math.E | 记录e的常量 |
Math.abs | 求绝对值 |
Math.sin | 正弦函数 |
Math.asin | 反正弦函数 |
Math.cos | 余弦函数 |
Math.acos | 反余弦函数 |
Math.tan | 正切函数 |
Math.atan | 反正切函数 |
Math.atan2 | 商的反正切函数 |
Math.toDegrees | 弧度转化为角度 |
Math.toRadians | 角度转化为弧度 |
Math.ceil | 得到不小于某数的最大整数 |
Math.floor | 得到不大于某数的最大整数 |
Math.IEEEremainder | 求余 |
Math.max | 求两数中最大值 |
Math.min | 求两数中最小值 |
Math.sqrt | 求开方 |
Math.pow | 求某数的任意次方, 抛出ArithmeticException处理溢出异常 |
Math.exp | 求e的任意次方 |
Math.log10 | 以10为底的对数 |
Math.log | 自然对数 |
Math.rint | 求距离某数最近的整数(返回double型) |
Math.round | 求距离某数最近的整数(返回int型或者long型) |
Math.random | 返回0,1之间的一个随机数 |
4、Random类
Java中存在着两种Random函数:
1、java.lang.Math.Random
调用这个Math.Random()函数能够返回带正号的double值,该值大于等于0.0且小于1.0,即取值范围是[0.0,1.0)的左闭右开区间,返回值是一个伪随机选择的数,在该范围内(近似)均匀分布。
2、java.util.Random
Random()的两种构造方法:
-
Random():创建一个新的随机数生成器。
-
Random(long seed):使用单个 long 种子创建一个新的随机数生成器。
在创建一个Random对象的时候可以给定任意一个合法的种子数,种子数只是随机算法的起源数字,和生成的随机数的区间没有任何关系。
public static void main(String[] args) { Random rand =new Random(); int i=rand.nextInt(100); }
在没带参数构造函数生成的Random对象的种子缺省是当前系统时间的毫秒数。
rand.nextInt(100)中的100是随机数的上限,产生的随机数为0-100的整数,不包括100。
【方法】
-
protected int next(int bits):生成下一个伪随机数。
-
boolean nextBoolean():返回下一个伪随机数,它是取自此随机数生成器序列的均匀分布的
boolean值。
-
void nextBytes(byte[] bytes):生成随机字节并将其置于用户提供的 byte 数组中。
-
double nextDouble():返回下一个伪随机数,它是取自此随机数生成器序列的、在0.0和1.0之间
均匀分布的 double值。
- flfloat nextFloat():返回下一个伪随机数,它是取自此随机数生成器序列的、在0.0和1.0之间均匀分
布flfloat值。
- double nextGaussian():返回下一个伪随机数,它是取自此随机数生成器序列的、呈高斯(
“正
态”)分布的double值,其平均值是0.0标准差是1.0。
-
int nextInt():返回下一个伪随机数,它是此随机数生成器的序列中均匀分布的 int 值。
-
int nextInt(int n):返回一个伪随机数,它是取自此随机数生成器序列的、在(包括和指定值(不
包括)之间均匀分布的int值。
-
long nextLong():返回下一个伪随机数,它是取自此随机数生成器序列的均匀分布的 long 值。
-
void setSeed(long seed):使用单个 long 种子设置此随机数生成器的种子。
5、日期时间类
5.1、Date类
Date 类提供两个构造函数来实例化 Date 对象。
第一个构造函数使用当前日期和时间来初始化对象。
Date();
第二个构造函数接收一个参数,该参数是从1970年1月1日起的毫秒数。
Date(long millisec);
方法 | 描述 |
---|---|
boolean after(Date date) | 若当调用此方法的Date对象在指定日期之后返回true,否则返回false |
boolean before(Date date) | 若当调用此方法的Date对象在指定日期之前返回true,否则返回false |
Object clone( ) | 返回此对象的副本 |
int compareTo(Date date) | 比较当调用此方法的Date对象和指定日期。两者相等时候返回0。调用对象在指定日期之前则返回负数。调用对象在指定日期之后则返回正数 |
int compareTo(Object obj) | 若obj是Date类型则操作等同于compareTo(Date) 。否则它抛出ClassCastException |
boolean equals(Object date) | 当调用此方法的Date对象和指定日期相等时候返回true,否 则返回false |
long getTime( ) | 返回自 1970 年 1 月 1 日 00:00:00 GMT 以来此 Date 对象表示的毫秒数 |
int hashCode( ) | 返回此对象的哈希码值 |
void setTime(long time) | 用自1970年1月1日00:00:00 GMT以后time毫秒数设置时间和日期 |
String toString( ) | 把此 Date 对象转换为以下形式的 String: dow mon dd hh:mm:ss zzz yyyy 其中: dow 是一周中的某一天 (Sun, Mon, Tue, Wed, Thu, Fri, Sat) |
5.2、SimpleDateFormat
【SimpleDateFormat格式化日期】
SimpleDateFormat 是一个以语言环境敏感的方式来格式化和分析日期的类。SimpleDateFormat 允许你选择任何用户自定义日期时间格式来运行
public static void main(String args[]) { Date dNow = new Date( ); SimpleDateFormat ft = new SimpleDateFormat ("yyyy-MM-dd hh:mm:ss"); System.out.println("当前时间为: " + ft.format(dNow)); }
其中 yyyy 是完整的公元年,MM 是月份,dd 是日期,HH:mm:ss 是时、分、秒
时间模式字符串用来指定时间格式:
字母 | 描述 | 示例 |
---|---|---|
G | 纪元标记 | AD |
y | 四位年份 | 2022 |
M | 月份 | July or 07 |
d | 一个月的日期 | 06 |
h | A.M./P.M.12小时 | 10 |
H | 24小时 | 14 |
m | 分 | 15 |
s | 秒 | 43 |
S | 毫秒 | 164 |
E | 星期 | Saturday |
D | 一年中第几天 | 300 |
F | 一个月第几周的周几 | 2 (second Wed. in July) |
w | 一年的第几周 | 50 |
W | 一个月的第几周 | 2 |
a | A.M./P.M.标记 | PM |
k | 24小时 | 23 |
K | A.M./P.M.12小时 | 09 |
z | 时区 | Eastern Standard Time |
' | 文字定界符 | Delimiter |
'' | 单引号 | ` |
【printf格式化日期】
public static void main(String args[]) { // 初始化 Date 对象 Date date = new Date(); //c的使用 System.out.printf("全部日期和时间信息:%tc%n",date); //f的使用 System.out.printf("年-月-日格式:%tF%n",date); //d的使用 System.out.printf("月/日/年格式:%tD%n",date); //r的使用 System.out.printf("HH:MM:SS PM格式(12时制):%tr%n",date); //t的使用 System.out.printf("HH:MM:SS格式(24时制):%tT%n",date); //R的使用 System.out.printf("HH:MM格式(24时制):%tR",date); }
【时间休眠】
sleep()使当前线程进入停滞状态(阻塞当前线程),让出CPU的使用、目的是不让当前线程独自霸占该进程所获的CPU资源,以留一定时间给其他线程执行的机会。
public static void main(String args[]) { try { System.out.println(new Date( ) + "\n"); Thread.sleep(1000*3); // 休眠3秒 System.out.println(new Date( ) + "\n"); } catch (Exception e) { System.out.println("Got an exception!"); } }
5.3、Calendar类
如何才能设置和获取日期数据的特定部分呢,比如说小时,日,或者分钟? 我们又如何在日期的这些部分加上或者减去值?使用Calendar类。
Calendar类的功能要比Date类强大很多,而且在实现方式上也比Date类要复杂一些。
Calendar类是一个抽象类,在实际使用时实现特定的子类的对象,创建对象的过程对程序员来说是透明的,只需要使用getInstance方法创建即可
创建一个代表系统当前日期的Calendar对象
Calender c = Calendar.getInstance();//默认是当前日期
创建一个指定日期的Calendar对象
//创建一个代表2022年7月01日的Calendar对象 Calendar c1 = Calendar.getInstance(); c1.set(2022, 7 - 1, 1);
Calendar类对象字段类型
常量 | 描述 |
---|---|
Calendar.YEAR | 年份 |
Calendar.MONTH | 月份 |
Calendar.DATE | 日期 |
Calendar.DAY_OF_MONTH | 日期,和上面的字段意义完全相同 |
Calendar.HOUR | 12小时 |
Calendar.HOUR_OF_DAY | 24小时 |
Calendar.MINUTE | 分钟 |
Calendar.SECOND | 秒 |
Calendar.DAY_OF_WEEK | 星期 |
设置完整日期
c1.set(2001, 1 - 1, 5);
设置某个字段
c1.set(Calendar.DATE,5); c1.set(Calendar.YEAR,2010);
add设置
c1.add(Calendar.DATE, 10); c1c1.add(Calendar.DATE, -10);
注意:Calender的月份是从0开始的,但日期和年份是从1开始的
6、String类
String 类代表字符串。Java 程序中的所有字符串字面值(如 "abc" )都作为此类的实例实现。 字符串是常量;它们的值在创建之后不能更改。字符串缓冲区支持可变的字符串。因为 String 对象是不可变的,所以可以共享。
public final class String implements java.io.Serializable, Comparable<String>, CharSequence { }
成员变量
//String的属性值 private final char value[]; //数组被使用的开始位置 private final int offset; //String中元素的个数 private final int count; //String类型的hash值 private int hash; // Default to 0 private static final long serialVersionUID = -6849794470754667710L; private static final ObjectStreamField[] serialPersistentFields = new ObjectStreamField[0];
从成员变量可以知道String类的值是fifinal类型的,不能被改变的,所以只要一个值改变就会生成一个新的String类型对象,存储String数据也不一定从数组的第0个元素开始的,而是从offffset所指的元素开始
String构造方法
String() //初始化一个新创建的 String 对象,使其表示一个空字符序列。 String(byte[] bytes) //通过使用平台的默认字符集解码指定的 byte 数组,构造一个新的 String。 String(byte[] bytes, Charset charset) //通过使用指定的 charset 解码指定的 byte 数组,构造一个新的 String。 String(byte[] bytes, int offset, int length) //通过使用平台的默认字符集解码指定的 byte 子数组,构造一个新的 String。 String(byte[] bytes, int offset, int length, Charset charset) //通过使用指定的 charset 解码指定的 byte 子数组,构造一个新的 String。 String(byte[] bytes, int offset, int length, String charsetName) //通过使用指定的字符集解码指定的 byte 子数组,构造一个新的 String。 String(byte[] bytes, String charsetName) //通过使用指定的 charset 解码指定的 byte 数组,构造一个新的 String。 String(char[] value) //分配一个新的 String,使其表示字符数组参数中当前包含的字符序列。 String(char[] value, int offset, int count) //分配一个新的 String,它包含取自字符数组参数一个子数组的字符。 String(int[] codePoints, int offset, int count) //分配一个新的 String,它包含 Unicode 代码点数组参数一个子数组的字符。 String(String original) //初始化一个新创建的 String 对象,使其表示一个与参数相同的字符序列;换句话说,新创建 的字符串是该参数字符串的副本。 String(StringBuffer buffer) //分配一个新的字符串,它包含字符串缓冲区参数中当前包含的字符序列。 String(StringBuilder builder) //分配一个新的字符串,它包含字符串生成器参数中当前包含的字符序列。
6.1、创建字符串对象方式
直接复制方式创建对象是在方法区的常量池
String str="hello";//直接赋值的方式
通过构造方法创建字符串对象是在堆内存
String str=new String("hello");//实例化的方式
==:
- 基本数据类型:比较的是基本数据类型的值是否相同
- 引用数据类型:比较的是引用数据类型的地址值是否相同
在字符串中,如果采用直接赋值的方式进行对象的实例化,则会将匿名对象“Lance”放入对象池,每当下一次对不同的对象进行直接赋值的时候会直接利用池中原有的匿名对象,我们可以用对象手工入池;
public static void main(String args[]){ String str =new String("Lance").intern(); //对匿名对象"hello"进行手工入池操作 String str1="Lance"; System.out.println(str==str1);//true }
-
直接赋值:只开辟一块堆内存空间,并且会自动入池,不会产生垃圾。
-
构造方法:会开辟两块堆内存空间,其中一块堆内存会变成垃圾被系统回收,而且不能够自动入池,需要通过public String intern();方法进行手工入池。
-
在开发的过程中不会采用构造方法进行字符串的实例化。
避免空指向
==在对字符串比较的时候,对比的是内存地址,而equals比较的是字符串内容,在开发的过程中,equals()通过接受参数,可以避免空指向。
String str = null; if(str.equals("hello")){//此时会出现空指向异常 ... } if("hello".equals(str)){//此时equals会处理null值,可以避免空指向异常 ... }
String类对象一旦声明则不可以改变;而改变的只是地址,原来的字符串还是存在的,并且产生垃圾
6.2、String常用方法
String的判断
boolean equals(Object obj)//比较字符串的内容是否相同 boolean equalsIgnoreCase(String str)// 比较字符串的内容是否相同,忽略大小写 boolean startsWith(String str)// 判断字符串对象是否以指定的str开头 boolean endsWith(String str)// 判断字符串对象是否以指定的str结尾
String的截取
int length()//获取字符串的长度,其实也就是字符个数 char charAt(int index)//获取指定索引处的字符 int indexOf(String str)//获取str在字符串对象中第一次出现的索引 String substring(int start)//从start开始截取字符串 String substring(int start,int end)//从start开始,到end结束截取字符串。包括start, 不包括end
String的转换
char[] toCharArray()//把字符串转换为字符数组 String toLowerCase()//把字符串转换为小写字符串 String toUpperCase()//把字符串转换为大写字符串
其他方法
String trim() //去除字符串两端空格 String[] split(String str)//按照指定符号分割字符串
6.3、String的不可变性
String太过常用,JAVA类库的设计者在实现时做了个小小的变化,即采用了享元模式,每当生成一个新内容的字符串时,他们都被添加到一个共享池中,当第二次再次生成同样内容的字符串实例时,就共享此对象,而不是创建一个新对象,但是这样的做法仅仅适合于通过=符号进行的初始化
在object中,equals()是用来比较内存地址的,但是String重写了equals()方法,用来比较内容的,即使是不同地址,只要内容一致,也会返回true
String不可变的好处
-
可以实现多个变量引用堆内存中的同一个字符串实例,避免创建的开销。
-
我们的程序中大量使用了String字符串,有可能是出于安全性考虑。
-
HashMap中key为String类型,如果可变将变的多么可怕。
-
当我们在传参的时候,使用不可变类不需要去考虑谁可能会修改其内部的值,如果使用可变类的话,可能需要每次记得重新拷贝出里面的值,性能会有一定的损失。
6.4、字符串常量池
- 常量池表
Class文件中存储所有常量(包括字符串)的table。这是Class文件中的内容,还不是运行时的内容,不要理解它是个池子,其实就是Class文件中的字节码指令
- 运行时常量池
JVM内存中方法区的一部分,这是运行时的内容。这部分内容(绝大部分)是随着JVM运行时候,从常量池转化而来,每个Class对应一个运行时常量池。绝大部分是因为:除了 Class中常量池内容,还可能包括动态生成并加入这里的内容。
- 字符串常量池
这部分也在方法区中,但与运行时常量池不是一个概念,字符串常量池是JVM实例全局共享的,全局只有一个。JVM规范要求进入这里的String实例叫“被驻留的interned string”,各个JVM可以有不同的实现,HotSpot是设置了一个哈希表StringTable来引用堆中的字符串实例,被引用就是被驻留。
享元模式
其实字符串常量池这个问题涉及到一个设计模式,叫“享元模式”,顾名思义 - - - > 共享元素模式。
也就是说:一个系统中如果有多处用到了相同的一个元素,那么我们应该只存储一份此元素,而让所有地方都引用这一个元素
Java中String部分就是根据享元模式设计的,而那个存储元素的地方就叫做“字符串常量池“
7、StringBuilder和StringBuffer
public final class StringBuilder extends AbstractStringBuilder implements java.io.Serializable, CharSequence{ }
StringBuilder 是一个可变的字符序列。它继承于AbstractStringBuilder,实现了CharSequence接口。
StringBuffffer 也是继承于AbstractStringBuilder的子类;但是,StringBuilder和StringBuffffer不同,前者是非线程安全的,后者是线程安全的
7.1、常用方法
- insert 插入
- append 追加
- replace 替换
- delete 删除
- index 查找位置
7.2、String、SringBuffer、StringBuilder的区别
-
String 字符串常量
-
StringBuffffer 字符串变量(线程安全)
-
StringBuilder 字符串变量(非线程安全)
在大多数情况下三者在执行速度方面的比较:StringBuilder > StringBuffffer > String
原因:
String 类型和 StringBuffffer 类型的主要性能区别其实在于 String 是不可变的对象, 因此在每次对 String类型进行改变的时候其实都等同于生成了一个新的 String 对象,然后将指针指向新的 String 对象,所以经常改变内容的字符串最好不要用 String ,因为每次生成对象都会对系统性能产生影响,特别当内存中无引用对象多了以后, JVM 的 GC 就会开始工作,那速度是一定会相当慢的。
是使用 StringBuffffer 类则结果就不一样了,每次结果都会对 StringBuffffer 对象本身进行操作,而不是生成新的对象,再改变对象引用。所以在一般情况下推荐使用 StringBuffffer ,特别是字符串对象经常改变的情况下。
总结:
-
如果要操作少量的数据用 = String
-
单线程操作字符串缓冲区下操作大量数据 = StringBuilder
-
多线程操作字符串缓冲区下操作大量数据 = StringBuffffer
8、File类
java.io.File类:文件和目录路径名的抽象表示形式
File类常见的构造方法:
public File(String pathname);
以pathname为路径创建File对象,如果pathname是相对路径,则默认的当前路径在系统属性user.dir
中存储。
-
File的静态属性String separator存储了当前系统的路径分隔符。
-
通过File对象可以访问文件的属性。
public boolean canRead() public boolean exists() public boolean isFile() public long lastModified() public String getName() public boolean canWrite() public boolean isDirectory() public boolean isHidden() public long length() public String getPath()
- 通过File对象创建空文件或目录(在该对象所指的文件或目录不存在的情况下)。
public boolean createNewFile()throws IOException public boolean delete() public boolean mkdir(), mkdirs()
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律