Java编程中是否可以继承String类?并说明原因。

发布网友 发布时间:2022-03-28 22:45

我来回答

4个回答

懂视网 时间:2022-03-29 03:07

string是Java的一种数据类型。在Java中,string类的定义为“public final class String extends Object”,其中包含final关键字,属于被final修饰的类,所以不能被继承。

  

  String对象是动态对象,需要创建对象实例后才能引用它的属性和方法。在创建一个String对象变量时,可以使用new运算符来创建,也可以直接将字符串赋给变量。

  

  Javascript具有5种基本数据类型,分别为number、string、null、boolean和undefined。此外还有一种复杂的数据类型:Object。Object本质上是由一组无序的名值对象组成的,如Date对象是一个日期和时间类型。

热心网友 时间:2022-03-29 00:15

String表示字符串,Java中所有字符串的字面值都是String类的实例,例如“ABC”。字符串是常量,在定义之后不能被改变,字符串缓冲区支持可变的字符串。因为 String 对象是不可变的,所以可以共享它们。例如:

String str = "abc";

相当于

char data[] = {'a', 'b', 'c'};
String str = new String(data);

这里还有一些其他使用字符串的例子:

System.out.println("abc");
String cde = "cde";
System.out.println("abc" + cde);
String c = "abc".substring(2,3);
String d = cde.substring(1, 2);

String类提供了检查字符序列中单个字符的方法,比如有比较字符串,搜索字符串,提取子字符串,创建一个字符串的副本、字符串的大小写转换等。实例映射是基于Character类中指定的Unicode标准的。 Java语言提供了对字符串连接运算符的特别支持(+),该符号也可用于将其他类型转换成字符串。字符串的连接实际上是通过StringBuffer或者StringBuilder的append()方法来实现的,字符串的转换通过toString方法实现,该方法由 Object 类定义,并可被 Java 中的所有类继承。 除非另有说明,传递一个空参数在这类构造函数或方法会导致NullPointerException异常被抛出。String表示一个字符串通过UTF-16(unicode)格式,补充字符通过代理对(参见Character类的 Unicode Character Representations 获取更多的信息)表示。索引值参考字符编码单元,所以补充字符在String中占两个位置。
定义 属性 构造方法
使用字符数组、字符串构造一个String
使用字节数组构造一个String
使用StringBuffer和StringBuider构造一个String
一个特殊的保护类型的构造方法
其他方法
getBytes
比较方法
hashCode
substring
replaceFirst、replaceAll、replace区别
copyValueOf 和 valueOf
intern
String对“+”的重载
String.valueOf和Integer.toString的区别
参考资料
一、定义
public final class String implements java.io.Serializable, Comparable<String>, CharSequence{}

从该类的声明中可以看出String是final类型的,表示该类不能被继承,同时该类实现了三个接口:java.io.Serializable、 Comparable<String>、 CharSequence

二、属性

private final char value[];

这是一个字符数组,并且是final类型,他用于存储字符串内容,从fianl这个关键字中可以看出,String的内容一旦被初始化了是不能被更改的。 虽然有这样的例子: String s = “a”; s = “b” 但是,这并不是对s的修改,而是重新指向了新的字符串, 从这里能知道,String其实就是用char[]实现的。

缓存字符串的hash Code,默认值为 0

因为String实现了Serializable接口,所以支持序列化和反序列化支持。Java的序列化机制是通过在运行时判断类的serialVersionUID来验证版本一致性的。在进行反序列化时,JVM会把传来的字节流中的serialVersionUID与本地相应实体(类)的serialVersionUID进行比较,如果相同就认为是一致的,可以进行反序列化,否则就会出现序列化版本不一致的异常(InvalidCastException)。
三、构造方法
String类作为一个java.lang包中比较常用的类,自然有很多重载的构造方法.在这里介绍几种典型的构造方法:
1.使用字符数组、字符串构造一个String
String就是使用字符数组(char[])实现的。所以可以使用一个字符数组来创建一个String,那么这里值得注意的是,当使用字符数组创建String的时候,会用到Arrays.copyOf方法和Arrays.copyOfRange方法。这两个方法是将原有的字符数组中的内容逐一的复制到String中的字符数组中。同样,也可以用一个String类型的对象来初始化一个String。这里将直接将源String中的value和hash两个属性直接赋值给目标String。因为String一旦定义之后是不可以改变的,所以也就不用担心改变源String的值会影响到目标String的值。
当然,在使用字符数组来创建一个新的String对象的时候,不仅可以使用整个字符数组,也可以使用字符数组的一部分,只要多传入两个参数int offset和int count就可以了。
2.使用字节数组构造一个String
在Java中,String实例中保存有一个char[]字符数组,char[]字符数组是以unicode码来存储的,String 和 char 为内存形式,byte是网络传输或存储的序列化形式。所以在很多传输和存储的过程中需要将byte[]数组和String进行相互转化。所以,String提供了一系列重载的构造方法来将一个字符数组转化成String,提到byte[]和String之间的相互转换就不得不关注编码问题。String(byte[] bytes, Charset charset)是指通过charset来解码指定的byte数组,将其解码成unicode的char[]数组,构造成新的String。
这里的bytes字节流是使用charset进行编码的,想要将他转换成unicode的char[]数组,而又保证不出现乱码,那就要指定其解码方式
同样使用字节数组来构造String也有很多种形式,按照是否指定解码方式分的话可以分为两种:

如果在使用byte[]构造String的时候,使用的是下面这四种构造方法(带有charsetName或者charset参数)的一种的话,那么就会使用StringCoding.decode方法进行解码,使用的解码的字符集就是指定的charsetName或者charset。在使用byte[]构造String的时候,如果没有指明解码使用的字符集的话,那么StringCoding的decode方法首先调用系统的默认编码格式,如果没有指定编码格式则默认使用ISO-8859-1编码格式进行编码操作。主要体现代码如下:

static char[] decode(byte[] ba, int off, int len) {
String csn = Charset.defaultCharset().name();
try {
// use charset name decode() variant which provides caching.
return decode(csn, ba, off, len);
} catch (UnsupportedEncodingException x) {
warnUnsupportedCharset(csn);
}
try {
return decode("ISO-8859-1", ba, off, len);
} catch (UnsupportedEncodingException x) {
// If this code is hit ring VM initialization, MessageUtils is
// the only way we will be able to get any kind of error message.
MessageUtils.err("ISO-8859-1 charset not available: "
+ x.toString());
// If we can not find ISO-8859-1 (a required encoding) then things
// are seriously wrong with the installation.
System.exit(1);
return null;
}
}
3.使用StringBuffer和StringBuider构造一个String

作为String的两个“兄弟”,StringBuffer和StringBuider也可以被当做构造String的参数。

public String(StringBuffer buffer) {
synchronized(buffer) {
this.value = Arrays.copyOf(buffer.getValue(), buffer.length());
}
}
public String(StringBuilder builder) {

this.value = Arrays.copyOf(builder.getValue(), builder.length());
}
当然,这两个构造方法是很少用到的,至少我从来没有使用过,因为有了StringBuffer或者StringBuilfer对象之后可以直接使用他们的toString方法来得到String。关于效率问题,Java的官方文档有提到说使用StringBuilder的toString方法会更快一些,原因是StringBuffer的toString方法是synchronized的,在牺牲了效率的情况下保证了线程安全。

public String toString() {

// Create a copy, don't share the array
return new String(value, 0, count);
}
this.value = Arrays.copyOfRange(value, offset, offset+count);

4.一个特殊的保护类型的构造方法

String除了提供了很多公有的供程序员使用的构造方法以外,还提供了一个保护类型的构造方法(Java 7):
String(char[] value, boolean share) {

// assert share : "unshared not supported";
this.value = value;
}
从代码中看出,该方法和 String(char[] value)有两点区别,第一个,该方法多了一个参数: boolean share,其实这个参数在方法体中根本没被使用,也给了注释,目前不支持使用false,只使用true。那么可以断定,加入这个share的只是为了区分于String(char[] value)方法,不加这个参数就没办法定义这个函数,只有参数不能才能进行重载。那么,第二个区别就是具体的方法实现不同。前面提到过,String(char[] value)方法在创建String的时候会用到Arrays的copyOf方法将value中的内容逐一复制到String当中,而这个String(char[] value, boolean share)方法则是直接将value的引用赋值给String的value。那么也就是说,这个方法构造出来的String和参数传过来的char[] value共享同一个数组。 那么,为什么Java会提供这样一个方法呢? 首先分析一下使用该构造函数的好处:

首先,性能好,这个很简单,一个是直接给数组赋值(相当于直接将String的value的指针指向char[]数组),一个是逐一拷贝。当然是直接赋值快了。
其次,共享内部数组节约内存但是,该方法之所以设置为protected,是因为一旦该方法设置为公有,在外面可以访问的话,那就破坏了字符串的不可变性。例如如下YY情形:
char[] arr = new char[] {'h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd'};

String s = new String(0, arr.length, arr); // "hello world"
arr[0] = 'a'; // replace the first character with 'a'
System.out.println(s); // aello world
如果构造方法没有对arr进行拷贝,那么其他人就可以在字符串外部修改该数组,由于它们引用的是同一个数组,因此对arr的修改就相当于修改了字符串。

所以,从安全性角度考虑,他也是安全的。对于调用他的方法来说,由于无论是原字符串还是新字符串,其value数组本身都是String对象的私有属性,从外部是无法访问的,因此对两个字符串来说都很安全。
在Java 7 之前有很多String里面的方法都使用这种“性能好的、节约内存的、安全”的构造函数。比如:substring、replace、concat、valueOf等方法(实际上他们使用的是public String(char[], int, int)方法,原理和本方法相同,已经被本方法取代)。
但是在Java 7中,substring已经不再使用这种“优秀”的方法了,为什么呢? 虽然这种方法有很多优点,但是他有一个致命的缺点,对于sun公司的程序员来说是一个零容忍的bug,那就是他很有可能造成内存泄露。 看一个例子,假设一个方法从某个地方(文件、数据库或网络)取得了一个很长的字符串,然后对其进行解析并提取其中的一小段内容,这种情况经常发生在网页抓取或进行日志分析的时候。下面是示例代码。
String aLongString = "...a very long string...";

String aPart = data.substring(20, 40);
return aPart;
在这里aLongString只是临时的,真正有用的是aPart,其长度只有20个字符,但是它的内部数组却是从aLongString那里共享的,因此虽然aLongString本身可以被回收,但它的内部数组却不能。这就导致了内存泄漏。如果一个程序中这种情况经常发生有可能会导致严重的后果,如内存溢出,或性能下降。

新的实现虽然损失了性能,而且浪费了一些存储空间,但却保证了字符串的内部数组可以和字符串对象一起被回收,从而防止发生内存泄漏,因此新的substring比原来的更健壮。

substring方法已经为了其鲁棒性放弃使用这种share数组的方法,但是这种share数组的方法还是有一些其他方法在使用的,这种方式构造对应有很多好处,其次呢,其他的方法不会将数组长度变短,也就不会有前面说的那种内存泄露的情况(内存泄露是指不用的内存没有办法被释放,比如说concat方法和replace方法,他们不会导致元数组中有大量空间不被使用,因为他们一个是拼接字符串,一个是替换字符串内容,不会将字符数组的长度变得很短!)。
四、其他方法
length() 返回字符串长度
isEmpty() 返回字符串是否为空
charAt(int index) 返回字符串中第(index+1)个字符
char[] toCharArray() 转化成字符数组
trim() 去掉两端空格
toUpperCase() 转化为大写
toLowerCase() 转化为小写
String concat(String str) //拼接字符串
String replace(char oldChar, char newChar) //将字符串中的oldChar字符换成newChar符
//以上两个方法都使用了String(char[] value, boolean share);
boolean matches(String regex) //判断字符串是否匹配给定的regex正则表达式
boolean contains(CharSequence s) //判断字符串是否包含字符序列s
String[] split(String regex, int limit) 按照字符regex将字符串分成limit份。
String[] split(String regex)

String string = "h,o,l,l,i,s,c,h,u,a,n,g";
String[] splitAll = string.split(",");
String[] splitFive = string.split(",",5);
splitAll = [h, o, l, l, i, s, c, h, u, a, n, g]
splitFive = [h, o, l, l, i,s,c,h,u,a,n,g]

热心网友 时间:2022-03-29 01:33

不可以,因为String是final类型,不可以继承,不可以修改

热心网友 时间:2022-03-29 03:07

不能,String是不能被继承的

声明声明:本网页内容为用户发布,旨在传播知识,不代表本网认同其观点,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。E-MAIL:11247931@qq.com