因为自己淋过雨,所以懂得为别人撑伞。
一、初步了解
1、什么是面向过程?
- 面向过程是一种以过程为中心的编程思想,是一种自顶向下的编程模式。
- 最典型的代表就是C语言。
- 在面向过程开发中,需要把问题分解成所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候一个一个依次调用就可以了。(例如 第一步要干什么、第二步要干什么、第三步要干什么…)
- 优点:代码流程化、执行效率高。
- 缺点:代码复用性低、扩展能力差、后期维护成本高。
2、什么是面向对象?
-
面相对象不是某一种语言的特性,而是一种编程思想。
-
面向对象编程的主要思想是把构成问题的各个事物分解成各个对象,建立对象的目的不是为了完成一个步骤,而是为了描述一个事物在解决问题的过程中经历的步骤和行为。
-
在面向对象的设计中,初始元素是对象(将程序和数据封装在对象中),然后将具有共同特征的对象归纳成类(物以类聚),组织类之间的等级关系,构造类库。在应用时,在类库中选择相应的类即可。
-
典型的代表有:Java、C++、C#、Python、PHP等等
-
优点:重用性、灵活性和扩展性高。
-
三大基本特征:封装、继承、多态。
-
五大基本原则:单一职责原则、开放封闭原则、里式替换原则、接口隔离原则、依赖倒置原则。
二、类
类:可以理解成图纸,它是对象共同特征的描述。
对象:是真实存在的具体实例。
在Java中,必须先设计类,才能创建对象并使用。
2.1 类的定义格式
public class 类名{
1、成员变量 (也可以称作属性,描述对象的特性)
2、成员方法 (包括:普通方法、静态方法,描述对象的行为)
3、构造器 (创建对象、初始化数据)
4、代码块 (包括:普通代码块{}、静态代码块static{})
5、内部类 (包括:成员内部类、局部内部类、匿名内部类和静态内部类)
}
注意事项:
- 类名首字母建议大写,满足驼峰命名规范。
- 一个Java文件中可以定义多个class类,但只能有一个类是public修饰,而且public修饰的类名必须和文件名保持一致。实际开发中建议还是一个文件定义一个class类。
- 成员变量的完整定义格式:修饰符 数据类型 变量名称 = 初始值; 一般无需指定初始值。
2.2 创建对象
使用 new
关键字来实例化一个对象(这种方式创建增加耦合度,无论使用什么框架,都要减少new的使用以降低耦合度)。
对象的功能:调用属性、调用方法。
创建对象语法格式如下:
类名 对象名 = new 类名();
例如:定义一个汽车类,属性有:名称、价格,行为有:启动、运行。
public class Car {
// 成员变量
String name;
double price;
public void start(){
System.out.println(name+"启动了");
}
public void run(){
System.out.println(price+"万的"+name+"跑起来了");
}
}
public class Test1 {
public static void main(String[] args) {
// 创建汽车对象
Car car = new Car();
// 调用属性
System.out.println(car.name);
System.out.println(car.price);
// 为属性赋值
car.name = "法拉利拉法";
car.price = 3150;
// 调用方法
car.start();
car.run();
}
}
2.3 对象在内存中的运行机制
两个对象的内存图
public class Car {
// 成员变量
String name;
double price;
public void start(){
System.out.println(name+"启动了");
}
public void run(){
System.out.println(price+"万的"+name+"跑起来了");
}
}
public class Test1 {
public static void main(String[] args) {
Car c1 = new Car();
c1.name = "法拉利拉法";
c1.price = 3150;
c1.start();
c1.run();
Car c2 = new Car();
c2.name = "宝马";
c2.price = 66;
c2.start();
c2.run();
}
}
- 对象放在堆内存中。
- 变量名c1存储是对象在堆内存中的地址。
- 成员变量的数据存放在对象中,即堆内存中。
两个变量指向同一个对象的内存图
public class Student {
String name;
char sex;
String hobby;
public void study(){
System.out.println(name+"正在学习");
}
// 打印个人信息
public void show(){
System.out.println("姓名:"+name+",性别:"+sex+",爱好:"+hobby);
}
}
public class Test2 {
public static void main(String[] args) {
Student s1 = new Student();
s1.name = "小张";
s1.sex = '男';
s1.hobby = "睡觉、摸鱼";
s1.show();
s1.study();
Student s2 = s1;
s2.name = "小李";
s2.sex = '女';
s2.hobby = "唱歌";
s2.show();
s2.study();
}
}
垃圾回收:当堆内存中的对象,没有被任何变量引用(指向)时,就会被判定为内存中的“垃圾”。
2.4 构造器
- 构造器也叫构造方法,用来实例化一个对象的。
- 构造方法的名称和类名相同,严格区分大小写,没有返回值。
- 构造器是在创建对象时被调用。
- 一个类可以有多个构造器,根据传入的参数(实参)匹配对应的构造器。
构造器的声明格式如下:
修饰符 类名([参数列表]){
语句;
}
1、如果我们没有显示的声明构造器,系统会默认提供一个无参构造器。
public class Dog{
String name;
int age;
void run(){
System.out.println(name+"在草坪上奔跑!");
}
// 无参构造器
// public Dog(){}
public static void main(String[] args){
Dog dog = new Dog();
}
}
2、如果我们显示的声明了有参构造器,当我们要使用无参构造器时,需要手动把这个无参构造器声明出来。
有参构造器:在一个对象初始化的时候,给指定的属性赋值。(往往为了简便开发)
所以我们要在这个类中把无参构造器声明出来:
public class Dog{
String name;
int age;
public Dog() {
System.out.println("无参构造器");
}
void run(){
System.out.println(name+"在草坪上奔跑!");
}
public Dog(String name){
this.name=name;
}
public static void main(String[] args){
// 实例化一个Dog对象
Dog dog = new Dog();
}
}
注:构造器不可以显示调用(用类名调用),构造器在创建对象时被配调用。
2.5 this关键字
- 代表当前对象 (可以获取当前类的属性和方法)。
- 用于区分局部变量和成员变量。
this([参数列表]);
用来调用本类的构造器。- 如果
this([参数列表]);
在构造中必须是第一条语句。
2.6 访问控制符
访问级别 | 访问权限修饰符 | 同类 | 同包 | 子类 | 全局范围内 |
---|---|---|---|---|---|
公开 | public | √ | √ | √ | √ |
受保护的 | protected | √ | √ | √ | x |
缺省 | √ | √ | x | x | |
私有 | pirvate | √ | x | x | x |
访问控制级别由小到大排序:private->缺省->protected->public。
2.7 封装
面向对象的三大特征:封装、继承、多态。
生活中封装的例子:手机、键盘等等
封装的规则:将类的成员隐藏在类的内部,这样外部程序无法直接访问,只能通过类提供的public方法去访问类中隐藏的成员。(合理暴露、合理隐藏)
封装的好处:
- 成员变量:私有化成员变量(private),提供公共的get/set方法,可以提高数据的安全性。
- 成员方法:将一段可以复用的功能封装到方法中,提高代码的复用性,降低我们的工作量。
public class Product {
// 私有化属性
private String name;
private double price;
/*
对属性进行封装,get/set
*/
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
// 无参构造器
public Product() {}
// 有参构造器
public Product(String name, double price) {
this.name = name;
this.price = price;
}
/**
* 封装一个方法:用于获取当前日期时间
*/
public String getDate(){
// 初始化Date对象
Date date = new Date();
return date.toString();
}
}
2.8 标准的JavaBean
JavaBean:也可以称为实体类,其对象可以用于在程序中封装数据。
标准JavaBean须满足如下书写要求:
- 成员变量使用private修饰。
- 提供成员变量对应的 set/get方法。
- 必须提供一个无参构造器;有参构造器可写可不写。
成员变量和局部变量的区别:
区别 | 成员变量 | 局部变量 |
---|---|---|
类中位置不同 | 类中,方法外 | 方法内 |
初始化值不同 | 有默认值,无需初始化 | 没有默认值,使用之前需要完成赋值 |
内存位置不同 | 堆内存 | 栈内存 |
生命周期不同 | 随着对象的创建而存在,随着对象的消失而消失 | 随着方法的调用而存在,随着方法的运行结束而消失 |
在所归属的大括号内{} |
2.9 final 关键字
-
final关键字是最终的意思,可以修饰类、方法、变量。
-
修饰类:表示该类是最终类,不能被继承。例如 :
public final class String{}
。 -
修饰方法:表示该方法是最终方法,不能被重写。(因此private方法默认是final型的)
-
修饰变量:使用前必须被初始化;一旦被初始化,将不能再被改变(常量)。
例如 final int AGE=18;
-
如果final修饰的变量是引用类型:那么变量存储的地址值不能发生改变,但是地址指向的对象内容是可以发生变化的。
2.10 枚举
枚举比常量更加严格,也是用来做信息标注和信息分类的。
1、枚举类的定义格式:
修饰符 enum 枚举名称{
枚举的第一行都是罗列枚举类的对象名称,建议全部大写。
}
例如:
public enum Season {
// 枚举类的第一行默认都是罗列枚举对象的名称,建议全部大写。
SPRING,SUMMER,AUTUMN,WINTER;
}
我们通过反编译查看枚举类的代码:
- 枚举类都是继承了枚举类型:java.lang.Enum类。
- 枚举类都是被final修饰,不能被继承。
- 枚举类的构造器都是私有的,对外不能创建对象。
- 枚举类的第一行默认都是罗列枚举对象的名称。
- 枚举类相当于是多例模式。
- 可以在switch语句中使用。类名.xxx
2、枚举的作用: 枚举可以做信息标志和分类(缺点:不能表示字面值,表示字面值用常量)
三、static关键字
-
staitc是静态的意思,可以修饰变量和方法。
-
在类中静态资源无法直接访问非静态的资源。
-
在静态方法中不能用this关键字。(因为java中的static是类区域,而this表示当前类的对象,然后static修饰的方法是由类直接调用,不需要创建对象,所以在static里不能用this)
-
static修饰的变量和方法不会被JVM自动回收。
-
静态方法不能被重写。
-
工具型的方法设计成static。(这样不依赖于某个对象,用类调用即可!)
-
static都会和final配合修饰常量。例如 public static final String USER_NAME=“张三”;
代码块
- 静态代码块:
static {...}
,随着类加载的时候执行,只执行一次,并且优先于各种代码块以及构造器,用于资源初始化。 - 普通代码块:
{...}
,是实例代码,依赖具体的对象,在创建对象时执行。
四、继承
4.1 继承的定义
-
Java中用关键字extends表示继承,我们可以让一个类和另一个类建立父子关系。
-
格式:
public Dog extends Animal {}
- Dog称为子类(也叫派生类),Animal称为父类(也叫基类或超类)。
-
作用:当子类继承父类后,就可以直接使用父类公有的属性和方法。
1、使用继承的好处
- 可以提高代码的复用性。(提取相同的特征)
- 增强类的功能扩展性。
2、继承设计规范
子类中相同的特征(共性属性和共性方法) 放在父类中定义,子类独有的属性和行为应该定义在子类自己里面。
先调用父类的无参构造器初始化父类数据,然后调用子类构造器(无参/有参),对象空间中先划分父类实例变量的内存,然后再划分子类实例变量内存,配合Java中单继承的约束 就可以保障每个类型的变量位置是固定不变的。
3、继承的特点
- 子类拥有父类非
private
关键字修饰的属性和方法,但是子类不能继承父类的构造器。 - Java中只能单继承,一个类只能继承一个父类。
- Java不支持多继承、但是支持多层继承。
- 子类可以扩展父类,拥有自己的属性和方法。
- 子类可以用自己的方式实现父类的方法。(重写方法)
- Java中所有的类都是Object类的子类。
4.2 方法重写
1、为什么需要重写?
父类的功能,子类不一定需要,或者功能不一定满足。(子类重写父类非static、private修饰的方法)
2、方法重写注意事项:
-
需要有继承关系,子类重写父类的方法。
-
方法名必须相同。
-
参数列表必须相同。
-
修饰符:范围可以扩大但不能缩小: public>protected>default>private。
-
抛出的异常:范围可以被缩小但不能扩大 ClassNotFoundException–>Exception(大的)。
-
方法重写,子类方法和父类方法必须一致,只是方法体不同。
3、方法重载和方法重写的区别
方法重载 | 方法重写 |
---|---|
overlead | override |
访问权限修饰符、返回值、异常无关 | 访问权限修饰符、异常、返回值有关 |
参数列表不同 | 方法名、参数列表相同 |
发生在同一个类 | 不同类 |
4.3 super关键字
- 可以在类中引用父类的属性和方法。
- 使用
super([参数列表]);
来调用父类的构造器。
this | super |
---|---|
指代当前对象 | 代表父类对象的引用 |
调用本类的构造器或方法和属性 | 调用父类的构造器或方法和属性 |
在构造器中必须是第一条语句 | 在子类构造器中必须是第一条语句 |
五、抽象类
抽象类:通俗来讲就是父类定义了一套规范让子类去实现。
- abstract 关键字 可以用来修饰方法也可以修饰类,如果修饰方法,那么该方法就是抽象方法;如果修饰类,那么该类就是抽象类。
修饰符 abstract class 类名{
修饰符 abstract 返回值类型 方法名(形参列表);
}
- 抽象类中可以没有抽象方法,但是有抽象方法的类一定要声明为抽象类。
- 抽象类,不能使用new关键字来创建对象,它是用来让子类继承的。
- 抽象方法,只有方法的声明,没有方法的实现,它是用来让子类实现的。
- 子类继承抽象类,那么就必须要实现抽象类没有实现的抽象方法,否则该子类也要声明为抽象类;
示例:
// 抽象类 abstract class
public abstract class Action {
/**
* 抽象方法
* 它没有方法体,它是一种约定,让别人来实现
*/
public abstract void doSth();
// 普通方法
public void run(){
System.out.println("小明在奔跑");
}
}
// 子类继承抽象类,那么就必须要实现抽象类没有实现的抽象方法,否则该子类也要声明为抽象类
class A extends Action{
@Override
public void doSth() {
System.out.println("小明在唱歌");
}
}
六、接口
JDK7以前:接口中只能放常量、抽象方法。
JDK8之后的版本:接口可以放常量、抽象方法、默认方法、静态方法。
-
使用interface关键字声明接口。例如: public interface UserDao{}
-
接口可以多实现(implement)和多继承(extends)
-
如果一个类实现了一个或多个接口,就必须重写接口中的所有抽象方法。(大概率的情况下:写接口、写实现类)
-
接口中所有的抽象方法可以省略
public abstract
。 -
接口中所有的变量都是常量,
public static final
,可以省略不写。 -
常量的命名规则:所有单词的字母全大写,中间以下划线分割。如STUDENT_STATUS
-
接口不能被实例化,因为接口中没有构造器。
示例:
// 接口
public interface MyInterface {
// 抽象方法
void add();
void delete();
void update();
void select();
}
// 实现接口,重写接口中的所有抽象方法
public class Application implements MyInterface{
@Override
public void add() {
System.out.println("添加");
}
@Override
public void delete() {
System.out.println("删除");
}
@Override
public void update() {
System.out.println("修改");
}
@Override
public void select() {
System.out.println("查询");
}
}
抽象类和接口的区别:
区别 | 抽象类 | 接口 |
---|---|---|
关键字 | abstract | interface |
成员变量 | 可以包含任意成员变量(包括各种访问级别的类成员变量和实例变量) | 只能包含公开的静态常量(默认由 public static final 修饰) |
方法 | 除了抽象方法外还可以包含任意的方法(包括各种访问级别的类方法和实例方法 ) | 只能包含公开的抽象方法(默认由 public abstract 修饰) |
使用 | 抽象类只能被继承(关键字extends),单继承 | 接口可以被实现(关键字implements)和继承 |
七、多态
多态指的是父类的某个方法被其子类重写时,可以各自产生各自的行为特征。
**多态存在的三个必要条件:继承/实现关系
,重写方法
,父类引用指向子类对象
。
示例:
//定义一个动物类作为父类
class Animal{
public void move() {
System.out.println("动物在移动!");
}
}
class Fish extends Animal{
// 重写了父类中的move方法
public void move() {
System.out.println("鱼儿在游!");
}
}
class Bird extends Animal{
// 重写了父类中的move方法
public void move() {
System.out.println("鸟儿在飞!");
}
}
class Horse extends Animal{
// 重写了父类中的move方法
public void move() {
System.out.println("马儿在跑!");
}
}
public class Application1 {
public static void main(String[] args) {
// 父类引用指向子类对象(向上转型)
Animal a1 = new Bird();
Animal a2 = new Fish();
Animal a3 = new Horse();
a1.move(); // 调用了Bird类的move方法 ,输出:鸟儿在飞!
a2.move(); // 调用了Fish类的move方法 ,输出:鱼儿在游!
a3.move(); // 调用了Horse类的move方法,输出:马儿在跑!
}
}
- 使用父类引用指向子类对象,该引用只能调用父类中定义的方法和属性。(自动类型转换)
- 调用方法时:编译看左边,运行看右边。
- 调用属性时:编译看左边,运行看左边。
- 如果子类中重写了父类中的一个方法,那么在调用这个方法的时候,将会调用子类中的这个方法。
- 父类对象指向子类引用,可以调用子类独有的功能。(强制类型转换)
- 多态是指方法的多态,属性没有多态。
- 使用
instanceof
关键字判断这个对象是否属于某个类。
八、String、StringBuffer、StringBulider
- java.lang.String类 代表字符串属于引用数据类型。
- 在Java中用双引号 “” 括起来的字面量都是String对象。例如 “你好世界”、"apple"等等
- Java中规定,字符串在创建之后不能改变 (因为它用final修饰的字符数组来存储数据),但是它们可以被共享。(每次拼接字符串,都是在常量池中创建新的字符串)
- 双引号括起来的字符串,例如:“abc”,“hello” 都是直接存储在方法区的
字符串常量池
当中。 - 为什么字符串要存储在方法区的字符串常量池中?
- 因为字符串在实际开发中使用太频繁,为了执行效率,所以把字符串放到了方法区的字符串常量池当中。
8.1 创建字符串对象
public class StringTest {
public static void main(String[] args) {
// 这两行代码表示底层创建了3个字符串对象,都在字符串常量池中
String s1 = "abc";
String s2 = "abc"+"def";
// 使用new的方式创建字符串对象
// 分析:这里的"def"是从哪里来的?
// 凡是双引号括起来的都在字符串常量池中有一份。
// new对象的时候一定在堆当中开辟空间。
String s3 = new String("def");
}
}
内存分配图如下:
8.2 字符串的比较
-
==
比较的是两个字符串对象。 -
String类中的equals()方法,比较存储在两个字符串对象中的内容是否相等。
-
equalsIgnoreCase()方法,忽略大小写比较字符串。
public class StringTest {
public static void main(String[] args) {
String s1 = "一起学Java";
String s2 = "一起学Java";
String s3 = "aaa";
// ==双等号比较的是内存地址
System.out.println(s1 == s2); // true
System.out.println(s2 == s3); // false
String x = new String("hello");
String y = new String("hello");
System.out.println(x == y); // false
String z = null;
System.out.println("password".equals(z));//false,这种写法避免空指针异常
System.out.println(z.equals("password")); //NullPointerException
}
}
源码分析:
思路:比较地址->比较长度->比较每一个字符。
8.3 String类的构造方法
方法 | 说明 |
---|---|
String() | 创建一个空字符串 |
String(byte[] bytes) | 根据字节数组构建一个字符串 |
String(char[] chars) | 根据字符数组构建一个字符串 |
String(String str) | 复制一个字符串 |
String(StringBuffer buffer) | 根据字符缓冲区构建字符串 |
String(StringBulider bulider) | 根据字符缓冲区构建字符串 |
使用最多的方式:接使用字面值的方式创建字符串,如:String str = "hello world"
。
8.4 String类常用方法
-
int length()
,返回字符串的长度; -
返回下标:
int indexOf(int ch)
, 返回指定数字在此字符串中的位置(下标); -
int indexOf(int ch, int fromIndex)
,返回指定数字在此字符串中的位置,从指定位置开始查找; -
int indexOf(String str)
,返回指定子字符在此字符串中的位置; -
int indexOf(String str,int fromIndex)
,返回指定子字符在此字符串中的位置,从指定位置开始查找; -
返回字符:
char charAt(int index)
,返回字符串中指定位置的字符; -
int lastIndexOf(int ch)
,返回指定字符在此字符串中最后一次出现处的索引,如果此字符串中没有这样的字符,则返回 -1; -
int lastIndexOf(int ch, int fromIndex)
,返回指定字符在此字符串中最后一次出现处的索引,从指定的索引处开始进行反向搜索,如果此字符串中没有这样的字符,则返回 -1; -
int lastIndexOf(String str)
,返回指定子字符串在此字符串中最右边出现处的索引,如果此字符串中没有这样的字符,则返回 -1; -
int lastIndexOf(String str, int fromIndex)
,返回指定子字符串在此字符串中最后一次出现处的索引,从指定的索引开始反向搜索,如果此字符串中没有这样的字符,则返回 -1; -
String trim()
,返回一个新字符串,这个字符串将删除原有字符串前后的半角空白字符(占一个字节,unicode编码为\u0020); -
String strip()
,返回一个新字符串,这个字符串将删除原有字符串前后的全角和半角空白字符(占两个字节,unicode编码为\u3000); -
bollean isEmpty()
,判断字符串是否为空; -
分割字符串:
String[] split(String regex)
,返回一个字符串数组,根据匹配给定的正则表达式来拆分字符串; -
String toLowerCase()
,返回新一个字符串,这个字符串将原始字符串中的大写字母改为小写; -
String toUpperCase()
,返回新一个字符串,这个字符串将原始字符串中的小写字母改为大写; -
截取字符串:
String substring(int beginIndex)
,返回新一个字符串,这个字符串包含原始字符串中从beginIndex(起始索引)到字符串的末尾的所有代码单元;(包括起始位置) -
String substring(int beginIndex, int endIndex)
,返回新一个字符串,这个字符串包含原始字符串中从beginIndex(起始索引)到字符串的endIndex-1的所有代码单元。(包括起始位置,不包括结尾位置) -
替换字符串:
String replace(char searchChar, char newChar)
,返回一个新字符串,用 newChar 字符替换原始字符串中出现的所有 searchChar 字符; -
判断当前字符串是否包含指定的字符:
boolean contains(CharSequence s)
。 -
字符串转字符数组:
char[] toCharArray()
。 -
判断字符串以什么字符开始:
boolean startsWith(String prefix)
。
JDK1.8 API在线文档:https://www.matools.com/api/java8
8.5 String练习
1、验证码
需求:随机生成一个5位的验证码,每位可能是数字、大写字母、小写字母。
分析: 1、定义一个字符串:存储a-zA-Z0-9之间的全部字符。
2、总共循环5次,每次获取随机范围内的索引,并将获取的字符拼接起来。
public class CaptchaTest {
public static void main(String[] args) {
String str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
Random r = new Random();
String code = "";
for (int i = 0; i < 5; i++) {
int index = r.nextInt(str.length());
char c = str.charAt(index);
code += c;
}
System.out.println(code); //aW2nF
}
}
2、模拟用户登录
需求:对用户登录进行校验,最多给3次机会。
public class LoginTest {
public static void main(String[] args) {
String okLoginName = "root";
String okPassword = "123456";
Scanner sc = new Scanner(System.in);
for (int i = 1; i <= 3 ; i++) {
System.out.println("用户名:");
String name = sc.next();
System.out.println("密码:");
String pwd = sc.next();
if (name.equals(okLoginName)&&pwd.equals(okPassword)){
System.out.println("登录成功");
break;
}else {
System.out.println(" 账号或密码错误 , 你还有"+(3-i)+"次机会!");
}
}
}
}
3、手机号码屏蔽
需求:接收一个11位的手机号,将中间四位号码屏蔽。例如:139* * * *59803
public class TelphoneTest {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.println("请输入一个11位的号码:");
String tel = sc.next();
// 获取前三位号码
String befor = tel.substring(0, 3); // [0,3)
// 获取后四位号码
String after = tel.substring(7); //从头截到尾
tel = befor + "****" + after;
System.out.println("屏蔽之后的手机号码为:"+tel);
}
}
8.6 StringBuffer
- StringBuffer 是一个字符串缓冲区,内容是可变的。
- 支持链式编程。
- 它是线程安全的 (StringBulider在单线程环境下效率更高)。
- 作用:提高字符串的操作效率,如拼接、修改等等。
- StringBuffer 与 String 是可以转换的。
- StringBuffer - > String : buffer.toString()。
- String - > StringBuffer : StringBuffer buffer = new StringBuffer(string)。
StringBuffer的构造方法
构造方法 | 说明 |
---|---|
public StringBuffer() | 构造一个不带字符的字符串缓冲区,保留16个字符的空间。 |
public StringBuffer(int[] capacity) | 构建一个不带字符,但具有指定初始容量的字符串缓冲区。 |
public StringBuffer(String str) | 构建一个字符串缓冲区,并将其内容初始化为指定的字符串内容。(初始容量为:16+参数str的长度) |
StringBuffer常用方法
方法 | 说明 |
---|---|
append(String str) | 用来在字符串末尾连接一个新的字符串str |
insert(int offest,String sub) | 在指定位置插入字符串sub。 |
delete(int beginIndex,int endIndex) | 用于删除从beginIndex开始到endIndex结束之间的字符串(含beginIndex,不包含 endindex) |
reverse() | 把StringBuffer内存反转 |
toString() | 把StirngBuffer变成String |
replace(int start,int end,String str) | 使用给定String中的字符替换此字符序列的字符串中的字符 |
length() | 返回StringBuffer的长度 |
public class TestStringBuffer {
public static void main(String[] args) {
// 创建一个StringBuffer对象
StringBuffer buffer = new StringBuffer("你不要留在过去");
// 向尾部追加数据
buffer.append(",");
buffer.append("因为明天一定会到来").append("。");//链式编程
System.out.println(buffer);
}
}
8.7 StringBulider
与 StringBuffer 基本相同,只是线程不安全(执行效率高)。
小结
- String 是 Java 中基础且重要的类,被声明为 final class,是不可变字符串。因为它的不可变性,所以拼接字符串时候会产生很多无用的中间对象,如果频繁的进行这样的操作对性能有所影响。
- StringBuffer 就是为了解决大量拼接字符串时产生很多中间对象问题而提供的一个类。它提供了 append()方法,可以将字符串添加到已有序列的末尾或指定位置,它的本质是一个线程安全的可修改的字符序列。
- 在很多情况下我们的字符串拼接操作不需要线程安全,所以 StringBuilder 登场了。StringBuilder 是 JDK1.5 发布的,它和 StringBuffer 本质上没什么区别,就是去掉了保证线程安全的那部分,减少了开销。
- 线程安全: StringBuffer线程安全,StringBuilder线程不安全。
- 速度:一般情况下,速度从快到慢为 StringBuilder > StringBuffer > String,当然这是相对的,不是绝对的。
- 使用场景:
- 操作少量的数据使用 String。
- 单线程操作大量数据使用 StringBuilder。
- 多线程操作大量数据使用 StringBuffer。
8.8 StringJoiner
JDK8出现的,简化字符串的拼接,使用的人很少。