2020-09-25
String的基本特性
#
-
String类的声明为final。
-
String实现了Serializable接口:表明字符串是支持序列化的。实现了Comparable接口:表面字符串是可以比较大小。
-
String再jdk8及以前使用 final char[] value存储字符串数据,jdk9改为byte[]。
-
String的字符串常量池是一个固定大小的hashtable,在jdk7中默认大小是60013,(jdk8中1009是可以设置的最小值),使用-XX:StringTableSize=可以设置StringTable的长度。如果字符串常量池中的字符串非常多,就可能会造成hash冲突,从而导致链表变得很长(链表长度大于8时会转化成红黑树),但还是会导致性能下降(比如在调用intern时)。
-
字符串的拼接操作
- 常量与常量的拼接是放在String Pool中,原因是编译期优化。
- 只要其中有一个是变量,结果就放在堆中。变量拼接的原理是StringBuilder。
- 如果拼接的结果调用intern方法,则主动将常量池中还没有的字符串放入池中,并返回其地址(如果String Pool中有,则直接返回其地址),下面还要对intern进行讨论。
字符串的拼接操作
#
String s1="a";
String s2="b";
String s3="ab";
String s4=s1+s2;
/*
如果被拼接的字符串中有变量,执行字符串拼接操作会进行如下几个步骤
①StringBuilder s=new StringBuilder();
②s.append(s1); s.append(s2);
③s.toString();
如果要被拼接的字符串中全是常量或者常量引用,则仍然使用编译器优化,不会涉及到上面三步。
*/
System.out.println(s4==s3);//false
部分源码分析
#
//下面这两行代码输出的结果为什么是nullabc呢?一起来分析一下源码吧
String s=null+"abc";
System.out.println(s);//nullabc
//StringBuilder类,当传入一个对象时会将该对象转化成一个字符串
public StringBuilder append(Object obj) {
return append(String.valueOf(obj));
}
//String类,当该对象为null时,会返回一个"null"字符串,否则返回该对象的toString方法
public static String valueOf(Object obj) {
return (obj == null) ? "null" : obj.toString();
}
部分考点分析
#
String s=new String("ab");
/*
创建了两个对象
对象1:new String("ab");
对象2:常量池中的"ab";
*/
String s1=new String("a")+new String("b");
/*
创建了6个对象
对象1:new StringBuilder();
对象2:new String("a");
对象3:常量池中的"a";
对象4:new String("b");
对象5:常量池中的"b";
对象6:当append操作结束后,会调用StringBuilder的toString方法,将StringBuilder对象转化为String对象,
此时又发生了一次new的操作:new String(value, 0, count);
tips:这里虽然创建了6个对象,但实际上在常量池中并没有创建"ab";
*/
intern
#
从jdk7开始,当我们调用String对象的intern()方法:
- 如果常量池中有这个字符串,则返回常量池中该串的地址。
- 如果常量池中没有该串,则会把对象的引用地址复制一份,放入常量池,并返回常量池中的引用地址。
String s1=new String("ab");
s1.intern();
String s2="ab";
System.out.println(s1==s2);//false;
/*
这是因为一个是堆中的对象,一个是常量池中的对象
*/
String s3=new String("a")+new String("b");
s3.intern();
String s4="ab";
System.out.println(s3==s4);//true(jdk7及以上版本的结果);
/*
这个是不是感觉很匪夷所思?
原因是在创建了s3之后,常量池中并没有"ab"这个对象,
而在执行s3.intern()后,常量池中多了一个指向堆中的对象的指针,
所以当执行s4="ab"时,s4实际上也是指向了堆中创建的那个对象。
为什么这么做呢?指针才占4个字节,用指针省空间。
*/
2020-09-22
定义
#
提供一个接口,让该接口负责创建一系列“相关或者相互依赖的对象”,无序指定他们具体的类。
理解
#
- 一系列相互依赖对象的创建。比如servlet使用 mysql对数据库处理的对象 或者 sqlserver对数据库处理的对象 ,由于mysql对应的类是不能使用sqlserver对应的类的,所以在这里我们就可以使用抽象工厂创建对象。
- 主要在于应对对“新系列”的需求变动。缺点在于难以应对“新对象”的需求变动。
类图
#

2020-09-22
定义
#
定义一个用于创建对象的接口,让子类决定实例化哪一个类,Factory Method使得一个类的实例化延迟。
理解
#
- 使用new违背了依赖倒置原则,导致程序间的紧耦合。
- 该模式隔离了对象的使用者和具体类型之间的耦合关系。面对一个经常变化的具体类型,紧耦合(new)会导致软件的脆弱。
- 通过面向对象的手法,将所要创建对象的工作延迟到了子类。从而实现了一种扩展的策略(而非更改),较好的解决了这种紧耦合的关系。
代码
#
//代表所有车
interface Car{
public void run();
}
//比亚迪
class Biyadi implements Car{
public void run(){
System.out.println("比亚迪run");
}
}
//奥迪
class Aodi implements Car{
public void run(){
System.out.println("奥迪run");
}
}
//工厂
interface CarFactory{
public void createCar();
}
//具体的实现
class BiyadiCarFactory implements CarFactory{
public void createCar(){
return new Biyadi();
}
}
class AodiCarFactory implements CarFactory{
public void createCar(){
return new Aodi();
}
}
场景
#
//使用工厂方法
public void travel(CarFactory carFactory){
//我开什么车去旅游全凭其他人给我什么车,假如公司有钱了,可能给我传递了更好的车
Car car=carFactory.createCar();
car.run();
}
/*虽然可能还是会有依赖,但是在本实现中依赖基本上已经消失了
其实依赖不可能完全消失,我们使用模式的时候可能只是将散落在程序各个部分的依赖都集中起来*/
//不使用工厂方法
public void travel(){
//我只能开比亚迪去旅游,不能被更改
Car car=new BiyadiCar();
car.run();
}
2020-09-21
定义
#
动态(组合)地给一个对象增加一些额外的职责。就增加功能而言,Decorator模式比生成子类(继承)更为灵活(消除重复代码&减少子类个数)。
理解
#
- java中的各种包装流就是用的装饰模式。
- 使用组合代替了不好的继承,使类的数量大大大的减少。
- 解决主体类在多个方向上的扩展功能。
案例
#
有一个类叫OutputStream,其完成一个打印的功能。
现在A想对该类进行扩充,使其有缓冲的功能。
B也想对该类进行扩充,使其输出更加安全。
C也想对该类进行扩充,使其输出到文件中。
这我们都可以通过类继承来完成,并且看上去似乎也没有什么问题。但是假如D也想对该类进行缓冲,使其既有缓冲,又更安全,这时候再使用类继承就不合适了。
此时我们可以使用装饰模式来解决该问题。

interface OutputStream{
public void print();
}
class ConcreteOutputStream implements OutputStream{
public void print(){}
}
//装饰器
abstract class Decorator implements OutputStream{
protected OutputStream outputStream;
public Decorator(OutputStream outputStream){
this.outputStream=outputStream;
}
public void print(){
outputStream.print();
}
}
//缓冲
class BufferedOutputStream extends Decorator{
public BufferedOutputStream(OutputStream outputStream){
super(outputStream);
}
public void print(){
System.out.println("I am BufferedOutputStream");
super.print();
}
}
//安全
class SecureOutputStream extends Decorator{
public SecureOutputStream(OutputStream outputStream){
super(outputStream);
}
public void print(){
System.out.println("I am SecureOutputStream");
super.print();
}
}
//输出到文件
class FileOutputStream extends Decorator{
public FileOutputStream(OutputStream outputStream){
super(outputStream);
}
public void print(){
System.out.println("I am FileOutputStream");
super.print();
}
}
2020-09-21
- 定义:定义一系列算法,把他们一个个封装起来,并且使它们可以互相替换(变化),该模式使得算法可独立于使用他的客户程序而变化。
- 在软件构建过程中,某些对象使用的算法可能多种多样,策略模式做到了运行时根据需要透明的更改对象的算法,将对象与自身解耦。
- 比如税率,如果要求一个程序支持多国税率,可以使用if-else语句。
class Sale{
public double buySomething(){
double d=xxx;
//...
if(tax==ChineseTax){
//...
}else if(tax==AmericeTax){
//...
}elseif(tax==JapanTax){
//...
}
//...
}
}
- 但是如果后期需要加上其他国家的税率,就要修改程序,违背了开闭原则。此时可以使用策略模式。实现如下:
interface Tax{
public double calculate(double money);
}
class ChineseTax implements Tax{
public double calculate(double money){
//...
}
}
class AmericaTax implements Tax{
public double calculate(double money){
//...
}
}
class Sale{
private Tax tax;
public Sale(Tax tax){this.tax=tax;}
public double buySomething(){
double d=xxx;
//...
tax.calculate(d);//替换掉先前的if-else
//...
}
}
2020-09-21
定义
#
定义一个操作中的算法的骨架(稳定),而将一些步骤延迟(变化)到子类,template method使得子类可以不改变(复用)一个算法的结构即可重定义(override重写)该算法的某些特定步骤。