JavaSE_第三周
接口
概念
微观概念:接口是一种能力和约定
接口的定义:代表了某种能力
方法的定义:能力的具体要求
宏观概念:接口是一种标准
经验:JAVA为单继承,当父类的方法种类无法满足子类需求时,可实现接口扩充子类能力
接口的语法
I.相当于特殊的抽象类,定义方式,组成部分,与抽象类类似
II.接口中只能定义公开静态常量
III.接口中只能定义公开抽象方法
IV.接口不是类
与抽象类的异同
I.相同:
1).可编译成字节码文件
2).不能创建对象
3).可以作为引用类型
4).具备Object中所定义的方法
II.不同:
1).所有属性都是公开静态常量,隐式使用public static final修饰
2).所有方法都是公开抽象方法,隐式使用public abstract修饰
3).没有构造方法,动态代码块,静态代码块
接口的规范
任何类在实现接口时,必须实现接口中所有的抽象方法,否则此类是抽象类。
实现接口中的抽象方法时,访问修饰符必须是public .
接口引用
同父类一样,接口也可声明为引用,并指向实现类对象。
接口多态
不再关注具体的类型,而是关注行为
不同的引用类型,仅可调用自身类型中所声明的方法
常见的关系
常见的关系:
类与类:
单继承
extends 父类名称
类与接口:
多实现
implements 接口名称1,接口名称2,接口名称n
接口与接口:
多继承
extends 父接口1,父接口2,父接口n
注:Java中任意类都继承自Object(默认)
常量接口
将多个常用于表示状态或固定值的变量,以静态常量的方式定义在接口中统一管理,提高代码可读性
接口回调原理
接口回调:先有接口的使用者,后有接口的调用者
需要传递实例对象,背后实质传递的是实现类对象中的实例方法,实例方法的背后实质是方法的逻辑代码
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9GtuGfnS-1615107952167)(接口回调.png)]
/**
* 接口/标准(排序)
*
* 只有实现此接口的对象才可以排序
*
*/
public interface Comparable<T> {
/**
* 比较的方法:
* this与传入的stu对象进行比较
* @param stu 另一个学生对象
* @return 标准: 正数,负数,零
* 负数:this靠前,stu靠后
* 正数:this靠后,stu靠前
* 零:不变
*/
public int compareTo(T stu);
}
/**
* 工具
*
*/
public class Tool {
/**
* 排序方法: 可以帮助任何类型的一组对象做排序
*/
public static void sort(Student[] stus) {
for(int i = 0; i < stus.length - 1; i++) {
Comparable currentStu = (Comparable)stus[i];
int n = currentStu.compareTo(stus[i + 1]);//正数 this靠后 接口的使用者
if(n > 0) {
Student temp = stus[i];
stus[i] = stus[i + 1];
stus[i + 1] = temp;
}
}
}
}
/**
* 接口回调:先有接口使用者,再有接口实现者
* 程序员
*/
public class TestCallback {
public static void main(String[] args) {
//需求:对一组学生对象排序
Student[] students = new Student[] {
new Student("tom",20,"male",99.0),
new Student("jack",21,"male",98.0),
new Student("annie",18,"female",100.0)};
// java.util.Arrays.sort(students);//Error 没有排序规则
//想要升序还是降序
// int n = students[0].compareTo(students[1]);
//工具调用者
Tool.sort(students);//默认升序
for(int i = 0; i < students.length; i++) {
System.out.println(students[i].name + "\t" + students[i].score);
}
}
}
class Student implements Comparable<Student>{//接口的实现者
String name;
int age;
String sex;
double score;
public Student() {}
public Student(String name, int age, String sex, double score) {
super();
this.name = name;
this.age = age;
this.sex = sex;
this.score = score;
}
public int compareTo(Student stu) {
if(this.score > stu.score) {
return 1;
}else if(this.score < stu.score) {
return -1;
}
return 0;
}
}
接口的好处
接口的好处:
程序的耦合度降低
更自然的使用多态
设计与实现完全分离
更容易搭建程序框架
更容易更换具体实现
关键字
interface
interface 接口名{}
注意:接口名的命名规则—需要遵循"大驼峰命名法",和类名的命名一致
接口的特点
1)接口中的功能是抽象方法,不能有方法体
2)接口不能实例化
3)如何实例化呢?
通过" 接口多态"
接口名 对象名 = new 子实现类名();
接口是通过子实现类实例化----前提—这个子实现类是一个具体类!
格式
具体的子实现类
开发中: class 接口名+impl implements 接口名{
}
最终:接口的子实现类一定是具体类,否则没有意义!
接口比抽象类更抽象!
抽象类—不能实例化!(不能创建对象)
抽象类的实例化是需要通过子类进行实例化的
抽象类名 对象名 = new 具体子类名() ; 抽象类多态
接口中成员特点
成员变量----此时常量
自定义常量:
public static final int a = 10 ;//基本数据类型 :编译时期常量(不需要Jvm加载的)
public static final Integer i = new Integer(10) ; //引用数据类型:运行常量(jvm需要加载的)
存在默认的修饰符-----public static final (可以省略不写)
成员方法
成员只能是抽象方法
存在默认修饰符:public abstract (可以省略) *
构造方法:
没有构造方法!
方法的形式参数以及返回值问题的研究
方法的形式参数
引用类型:
数组:实际参数需要传递当前数组对象
类:
具体类:实际参数传递,需要该具体类对象(要么直接匿名对象,要么 具体类名 对象名 = new 具体类名();)
抽象类:实际参数传递,需要该抽象类的子类对象(通过抽象类多态的形式 :该抽象类的子类对象(具体类) 或匿名内部类)
接口:实际参数传递,需要该接口的子实现类对象(通过接口多态的形式:接口的子实现类对象或匿名内部 类)
方法形式参数:如果是引用类型(除过String),形式参数的改变会直接影响实际参数!
方法的返回值是引用数类型
类:
具体类:方法需要结束,返回的是当前具体类型----有一个空间地址值(匿名对象/有名字对象 类名 对象 名 = new 类名();)
抽象类:方法需要结束,返回的是当前抽象类的子类对象----定义一个子类继承抽象类或匿名内部类
接口类型:方法需要结束,返回的是当前接口的子实现类对象---定义接口的子实现类实现接口(接口多态)或匿 名内部类
面试题----接口和抽象类的区别
1)成员的区别
抽象类:
成员变量:可以是常量,也可是变量
成员方法:可以是抽象方法,也可以是非抽象方法
构造方法:存在的,有参构造/无参构造---给数据进行初始化
接口:
成员变量:只能是常量
存在默认的修饰符:public static final (可以省略)
成员方法:只能是抽象方法
存在默认的修饰符 : public abstract(可以省略)
构造方法:
不存在
2)关系的区别
类和类的关系:
继承关系:extends
只能支持单继承,不支持多继承,但是可以多层继承!
类和接口的关系
实现关系:implements
一个类可以实现多个接口---->也可以继承某个类的同时可以实现多个接口
接口与接口的关系
继承关系:extends
既可以单继承,也可多继承
3)设计理念的区别
抽象类:存在继承关系,体现的是一种"is a"的关系
接口: 提供的一种额外的功能,本身不具备的,只有实现了接口才具备这个功能,体现的一种"like a"的关系!
修饰符
状态修饰符
final:最终的,无法更改的!
-
可以修饰类,当前类不能被继承
-
可以修饰变量,此时这个变量是一个常量
-
可以修饰成员方法,此时这个方法不能被重写!
静态修饰符
static: 共享(共用!)
-
随着类/接口的加载而加载
-
优先于对象存在 (不能this关键字)
-
被static修饰 的成员变量,可以被多个对象共用
-
被static修饰的,它的访问方法是 推荐--->使用类名/接口名.变量;/方法名();
抽象修饰符
abstract:抽象
-
可以修饰类----抽象类(描述:现实世界事物中,比较概括性的东西)
-
可以修饰成员方法---->抽象方法
-
可以在抽象类中使用
-
也可以接口中使用
权限修饰符
在同一个包下的本类中 在同一个包下的子类(无关类中) 不同包下的子类中 不同包下无关类中
public Y Y Y Y
protected Y Y Y
默认修饰符 Y Y
private Y
开发中,优先使用public—访问权限足够大的
其次是protected---->我们自定义的时候很少写,一般都是在源码中
内部类
什么是内部类
一个类中定义另一个了类!
举例: 在类A中定义了一个类B,那么将类B称为类A的内部类,类A是当前类B的外部类
内部类的特点
编译后可生成独立的字节码文件
内部类中的成员可以访问外部类的成员,包括私有!
外部类的成员访问内部类(成员内部类)中的成员方法,需要创建内部类对象来访问!
内部类的分类
成员内部类
静态内部类
局部内部类
匿名内部类
成员内部类
在类的内部定义,与实例变量实例方法同级别的类
外部类的一个实例部分,创建内部类对象时,必须依赖外部类对象
当外部类和内部类存在重名属性时,会优先访问内部类属性
成员内部类不能定义静态成员
特点
1)它的成员可以访问外部类的成员,包括私有
2)测试类中,访问当前成员内部类中的成员方法
前提条件:当前成员内部类:是非静态的!
格式:
外部类名.内部类名 对象名 = 外部类对象.内部类对象;
成员内部类中可以加入修饰符
为了保证数据的安全性,在成员内部类加入私有修饰符:private
在翻阅源码中–会有私有的成员内部类!
public class TestMemberClass {
public static void main(String[] args) {
// Inner in = new Inner();//Error 不能脱离外部类对象独立存在
Outer out = new Outer();
//创建成员内部类对象(必须依赖外部类对象)
Outer.Inner in = out.new Inner();
// System.out.println(in.b);
in.m2();
// System.out.println(out.a);//Error 封装依旧有效,无法从外部直接访问
// Outer.Inner.field;//Error 无法脱离外部类对象进行静态的调用
}
}
class Outer{
private int a = 10;
//成员内部类,依赖外部类对象
class Inner{
int a = 20;
/*static*/ String field = "abc";//成员内部类不能定义静态成员
public void m2() {
int a = 30;
System.out.println("Inner -- m2() " + a);//内部类可以访问自身的局部变量
System.out.println("Inner -- m2() " + this.a);//内部类可以访问内部类实例变量
System.out.println("Inner -- m2() " + Outer.this.a);//内部类可以访问外部类的实例变量
}
}
}
静态内部类
不依赖外部类对象,可直接创建或通过类名访问,可声明静态成员
只能直接访问外部类的静态成员(实例成员需实例化外部类对象)
特点
静态的成员内部类中的成员方法(静态/非静态)只能访问外部类的静态的成员,否则报错!
非静态的成员内部类中不能出现静态的成员!
public class TestStaticClass {
public static void main(String[] args) {
//外部类的静态属性
System.out.println(Outer.a);
//外部类的静态方法
Outer.m1();
//创建静态内部类对象时,可直接通过完整名称进行创建
Outer.Inter in = new Outer.Inter();//静态内部类的创建,不依赖外部类的对象
//创建内部类对象后,访问实例属性和方法
System.out.println(in.b);
in.m2();
//额外比成员内部类多了一个定义的内容(静态属性和静态方法)
//通过外部类类名.内部类类名.内部类的静态属性
System.out.println(Outer.Inter.field);
//通过外部类的类名.内部类类名.内部类的静态方法
Outer.Inter.m3();
}
}
class Outer{
static int a = 10;
int c = 30;
static class Inter{
int b = 20;
static String field = "abc";
public void m2() {
System.out.println("Inter--m2()");
// System.out.println(c);
}
public static void m3() {
System.out.println(a);
}
}
static void m1(){
System.out.println("Outer--m1()");
}
}
局部内部类
定义在外部类方法中,作用范围和创建对象范围仅限于当前方法
特点
1)局部内部类的成员都可以访问外部类的成员,包括私有
2)外部类的成员要访问局部内部类中的成员,通过对象访问 内部类名 对象名 = new 内部类名() ;
JDK8+后新特性
局部内部类访问外部类当前方法的局部变量时,因无法保障变量的生命周期与自身相同,变量必须修饰为final
public class TestLocalClass {
public static void main(String[] args) {
Outer out = new Outer();
out.m1();
}
}
class Outer{
int a = 10;
public void m1() {
Printable pr = null;
final String local = "Hello";//从JDK8+后,默认在此种情况下添加final关键字
class Inter implements Printable{
int a = 20;
@Override
public void print() {
int a = 30;
System.out.println("Inter -- m1()" + local);
}
}
pr = new Inter();
pr.print();
}
}
interface Printable{
public void print();
}
匿名内部类
没有类名的局部内部类
使用格式
new 类名/接口名(){
//重写方法;
} ;
类名–可以是抽象类也可以是具体类(用的少)
位置----局部位置使用
匿名内部类的本质是什么?
本质就是继承了该抽象类或者是实现了该接口的子类对象
public class TestAnonymity {
public static void main(String[] args) {
// Animal a = new Dog();
// a.eat();
//
// Animal a2 = new Animal() {
// String furColor;
//
// public void eat() {
// System.out.println("狗在啃狗头...");
// }
// };
//
// a2.eat();
//
// System.out.println(a2);
Swimmable s1 = new Swimmable() {
@Override
public void swim() {
System.out.println("会游泳的...");
}
};
s1.swim();
System.out.println(s1);
}
}
//class Animal{
// public void eat() {
// System.out.println("动物在吃...");
// }
//}
//
//class Dog extends Animal{
// public void eat() {
// System.out.println("狗在啃骨头...");
// }
//}
interface Swimmable{
public void swim();
}
使用范围
使用范围从小到大:匿名内部类 = 局部内部类 < 私有成员内部类 < 私有静态内部类 < 非私有成员内部类 < 非私有静态内部类
匿名内部类在开发中的使用
方法的形式参数如果是抽象类以及接口,实际参数如何传递
如果是抽象类---需要该抽象类的子类对象
1)要么定义具体的子类 ..
2)要么使用抽象类的匿名内部类
接口---->需要传递该接口的子实现类对象
1)要么定义接口的子实现类
2)要么使用接口的匿名内部类
方法的返回值如果是抽象类以及接口,如何返回?
返回如果是抽象类,返回的需要该抽象类的子类对象
1)要么定义具体的子类 .. (匿名对象: new 子类名() )
2)要么使用抽象类的匿名内部类
返回如果是接口类型,返回的需要该接口的子实现类对象
1)要么定义接口的子实现类
2)要么使用接口的匿名内部类
匿名内部类的面试题
//面试题补全代码:
// 在控制台输出"helloworld"
interface Inter{
void show() ;
}
class Outer{
//补全代码:
public static Inter method(){ //返回值是接口类型
//返回的是该接口子实现类对象---->接口的匿名内部类
return new Inter(){
@Override
public void show() {
System.out.println("helloworld");
}
} ;
}
}
class InnerClassTest3{
public static void main(String[] args){
//给的代码
Outer.method().show();
//Outer.method()---->说明这个method方法是静态的 类名.方法名()
//Outer.method().show();method方法调用完毕之后,是有返回值类型的方法----.show()---->接口中的方法
//获取到接口的子实现类对象.show();
}
}
外部类的this限定
//面试题
// 请阅读代码,分别在控制台上输出30,20,10,补全代码
/**
* 在内部类中,不存在继承关系
* 变量名称一致的时候,遵循"就近原则"
*/
class Outer2{
public int num = 10 ;
//成员内部类
class Inner2{
public int num = 20 ;
public void show(){
int num = 30 ;
//补全代码
System.out.println(num);
System.out.println(this.num);//代表是当前类对象的地址值引用
System.out.println(new Outer2().num); //访问外部类中的num---new 外部类名().num;
System.out.println("--------------------");
//外部类的this限定
//外部类名.this.变量名:访问外部类中的成员变量
System.out.println(Outer2.this.num);
}
}
}
public class Test3 {
public static void main(String[] args) {
//给了代码
Outer2.Inner2 oi = new Outer2().new Inner2() ;
oi.show();
}
}
API
学习Java中的常用类---常用的功能---在实际开发中都会用到
Application Programming interface:应用程序接口文档
以后在开发中,学习各种各样的文档
模拟百度地图定位系统-------百度地图---接口文档
模拟人脸识别系统----- 百度Ai ----开发接口文档
模拟微信分享功能----微信分享----接口文档
Object类
超类,基类,所有类的直接或间接父类,位于继承树的最顶层
任何类,如果没有书写 extends 显示继承某个类,都默认直接继承Object类,否则为间接继承
Object类中所定义的方法,是所有对象都具备的方法
Object类型可以存储任何对象
作为参数可接受任何对象
作为返回值可返回任何对象
常用方法
getClass()
public final Class<?> getClass(){}表示正在运行的是的类----->字节码文件对象(Class类对象)
返回引用中存储的实际对象类型
获取到的这个Class----class 包名.类名
Object类中的源码 getClass
public final native Class getClass();
native:本地方法 --- 非Java语言实现 (由Java调用非Java代码的接口) 底层都是c或者是C++
JNI:Java Native Interface:Java本地接口---- Java语言可以调用C语言
应用:通常判断两个引用中实际存储对象类型是否一致
hashCode()
public int hashCode(){}
返回该对象的十进制的哈希码值---由哈希表算出来的
哈希算法根据对象的地址或字符串或数字计算出来的int类型的数值
哈希码并不唯一,可保证相同对象返回相同哈希码,尽量保证不同对象返回不同的哈希码
toString()
public String toString(){}
返回该对象的字符串表现形式,通常,toString 方法会返回一个“以文本方式表示”此对象的字符串。
可以根据程序需求覆盖该方法,如:展示对象的各个属性
equals()
public boolean equals(Object obj){} 指示其他某个对象是否与此对象“相等”
默认实现(this == obj),比较两个对象的地址是否相同
可进行覆盖,比较两个对象的内容是否相同
equals()方法覆盖步骤:
比较两个引用是否指向同一个对象
判断obj是否是null
判断两个引用指向的实际对象类型是否一致
强制类型转换
依次比较各个属性值是否相同
注:成员信息一样,认为应该是同一个人,所以要比较两个对象的内容是否相同,必须重写Object类中的equals方法,
当此方法被重写时,通常有必要重写 hashCode 方法,以维护 hashCode 方法的常规协定,该协定声明相等对象(内容相等)必须具有相等的哈希码。
==和equals的区别:
运算符号: 可以连接两个基本数据类型,基本数据类型比较的是两个数值是否相同!
而如果==连接的是两个引用数据类型(对象),比较的是两个对象的地址值是否相同!
equals方法:默认执行的是Object类中的equals方法,比较的是两个对象的地址值是否相同;
如果重写了equals方法,比较的是两个对象的内容是否相同(成员变量信息是否一致)!
finalize()
当对象被判定为垃圾对象时,由JVM自动调用此方法,用以标记垃圾对象进入回收机制
垃圾对象:没有有效引用指向此对象时,为垃圾对象
垃圾回收:由GC销毁垃圾对象,释放数据存储空间
自动回收机制:JVM的内存耗尽,一次性回收所有垃圾对象
手动回收机制:使用System.gc(),通知JVM执行垃圾回收
clone()
protected Object clone() throws CloneNotSupportedException
创建并返回此对象的一个副本
Object 类的 clone 方法执行特定的复制操作。
首先,如果此对象的类不能实现接口 Cloneable,则会抛出 CloneNotSupportedException。
注:所有的数组都被视为实现接口 Cloneable
通过clone方法复制 字段(属性)----浅克隆,针对对象的字段(属性)进行复制,不会创建新的对象
Scanner类
文本扫描器:Scanner ---- java.util.Scanner;
//创建键盘录入对象
Scanner sc = new Scanner(System.in) ;
构造方法
public Scanner(InputStream source):创建一个文本扫描器,通过字节输入流读取输入的数据
形式参数是一个抽象类---实际参数传递需要该抽象类的子类对象
成员方法
public boolean hasNextInt()
public boolean hasNextLine()
public boolean hasNextByte()
...
public boolean hasNextXXX() :判断下一个数据录入的是否为xxx数据类型
public boolean haxNextLine();判断下一个数据是否为字符串类型
public xxx nextXXX() :接收数据的方法
public String nextLine()录入的是字符串类型
public class ScannerDemo {
public static void main(String[] args) {
//System---->有一个静态的字段in----标准输入流
//static final InputStream in
//字段的类型:InputStream
InputStream inputStream = System.in ;
// public Scanner(InputStream source):
Scanner sc = new Scanner(inputStream) ;
//录入一个int类型数据
// public int nextInt() //录入int类型数据
/*System.out.println("请您录入一个int类型数据:");
int num = sc.nextInt() ; //InputMismatchException:用户输入的类型和接收的类型不匹配,出现异常!
System.out.println("您录入的数据是:"+num);*/
//加入Scanner类提供的判断功能
System.out.println("请您输入一个数据:");
//public boolean hasNextXXX()
if(sc.hasNextInt()){ //判断下一个录入的是否为int类型
//true
//使用int来接收
int num =sc.nextInt() ;
System.out.println("您录入的是一个数据为:"+num);
}else if(sc.hasNextLine()){//判断下一个录入的是否为String
String line =sc.nextLine() ;
System.out.println("您录入的数据为:"+line);
}else{
System.out.println("对不起,没有提供这个录入的功能...");
}
}
}
键盘录入数据的细节
先录入int类型,在录入String类型:字符串数据被漏掉了
原因就是:回车符号的问题
解决方案:
1)使用Scanner中---public String next() 录入字符串 (非正式用法)
2)在录入String类型的时候,可以创建一个新的Scanner对象
String类
String 类代表字符串。Java 程序中的所有字符串字面值(如 “abc” )都作为此类的实例实现。
字符串是常量;它们的值在创建之后不能更改。
构造方法
String() :构造一个空字符序列
public String(byte[] bytes):将字节数组构造成一个字符串(解码的过程-平台默认的解码格式:UTF-8)
public String(byte[] bytes,int offset,int length):将一个部分字节数组构造成字符串
参数1:字节数组对象
参数2:指定的角标
参数3:指定的长度
public String(char[] value):将字符数组构造成字符串
public String(char[] value,int offset,int count):将字符数组的一部分构造成字符串
public String(String original):将某个字符串数据常量--构造成String对象
特点
字符串是常量;它们的值在创建之后不能更改。
形式参数如果是基本数据类型,形式参数的改变对实际参数没有影响
形式参数如果引用数据类型,形式参数的改变会直接影响实际参数! (除过String类型:作为形式参数和基本类型的效果一样)
public class StringDemo2 {
public static void main(String[] args) {
String s = "hello" ; //将常量赋值给变量s
s+="qianfeng" ;
System.out.println(s);//helloqianfeng
System.out.println("----------------------------");
int a = 20 ; //实际参数
int b = 30 ;
System.out.println("a:"+a+",b:"+b);//a:20,b:30
change(a,b) ;
System.out.println("a:"+a+",b:"+b);//a:20 ,b:30
System.out.println("===================================================");
String str = "hello" ;
System.out.println(str);//hello
change(str) ;
System.out.println(str);//hello
}
public static void change(String s){ //形式参数是引用类型:String=-->特殊的引用类型
System.out.println(s);
s+="worldjavaee" ;
System.out.println(s);
}
public static void change(int a, int b) {//形式参数: 局部变量随着方法调用而存在,随着方法调用结束而消失
System.out.println("a:"+a+",b:"+b);//a:20,b:30
a = b ;
b = a+ b ;
System.out.println("a:"+a+",b:"+b);//a:30,60
}
}
判断功能
public boolean equals(Object anObject):和指定的字符串进行比较(字符串重写了Object类中的equals方法):比较的是字符串内容是否相同
public boolean equalsIgnoreCase(String anotherString):不区分大小写比较
public boolean contains(String s/字符序列):判断是否包含指定的字符序列
public boolean endsWith(String suffix):判断当前此字符串是否以指定的子字符串结尾
public boolean startsWith(String prefix):判断当前此字符串是否以指定的子字符串开头
public boolean isEmpty():判断此字符串是否为空
获取功能
public char charAt(int index):获取指定索引处的字符
public String concat(String str):将指定的字符串和此字符串进行拼接,获取一个新的字符串
public int indexOf(String str):获取子字符串在当前字符串中第一次出现的索引值
public int lastIndexOf(String str):获取子字符串在当前字符串最后一次出现的索引值
public int length():获取字符串的长度
public String substring(int beginIndex):从指定位置开始默认截取到末尾,获取当前字符串
public String substring(int beginIndex,int endIndex):从指定位置开始截取到指定位置结束,包左不包右 beginIndex,endIndex-1处
字符串的遍历
public class StringDemo3 {
public static void main(String[] args) {
//创建键盘录入对象
Scanner sc = new Scanner(System.in) ;
//提示并录入数据
System.out.println("请您录入一个字符串数据:");
String line = sc.nextLine() ;
//遍历字符串:获取字符串中的每一个字符值:charAt()和length()相结合
for(int x = 0 ; x < line.length() ; x++){
// System.out.println(line.charAt(x));
char ch = line.charAt(x) ;
System.out.print(ch+" ");
}
}
}
转换功能
String类的转换功能
public byte[] getBytes():编码过程(将能看懂的东西转换成看不懂的):将字符串转换字节数组
public char[] toCharArray():将字符串转换成字符数组
public String[] split(String regex):将字符串转换成字符串数组(分割/拆分功能)
public String toLowerCase():将字符串转换小写
public String toUpperCase():将字符串转换成大写
public static String valueOf(int/long/float/double/boolean/Object):万能方法,
将一些部分基本数据类型和引用类型转换成String
public class StringDemo {
public static void main(String[] args) {
//定义一个字符串
String str = "hello" ;
//public byte[] getBytes():编码过程(将能看懂的东西转换成看不懂的):将字符串转换字节数组
byte[] bytes = str.getBytes();// 无参方法:平台默认的编码集: 当前idea:UTF-8
// System.out.println(bytes);
for(int x = 0 ; x < bytes.length ; x ++){
System.out.println(bytes[x]); //字符串中的字符---寻找对应的ASCII码表中的值
}
System.out.println("--------------------------------------------------");
// public char[] toCharArray():将字符串转换成字符数组
char[] chars = str.toCharArray();
for(int x = 0 ; x < chars.length ; x ++){
char ch = chars[x] ;
System.out.println(ch);
}
System.out.println("--------------------------------------------------");
//public String[] split(String regex):将字符串转换成字符数组(分割/拆分功能)
String s = "Java-C-Php-Go-R-Hadoop" ;
String[] strArray = s.split("-");
//遍历字符串数组
for(int x = 0 ; x < strArray.length ; x ++){
System.out.println(strArray[x]);
}
System.out.println("--------------------------------------------------");
/**
* public String toLowerCase():将字符串转换小写
* public String toUpperCase():将字符串转换成大写
*/
System.out.println("toLowerCase():"+str.toLowerCase());
System.out.println("toUpperCase():"+str.toUpperCase());
System.out.println("--------------------------------------------------");
// public static String valueOf(int/long/float/double/boolean/Object):
int i = 100 ;
//int---- Integer(int类型的包装类类型) ---String
String result = String.valueOf(i);
System.out.println("result:"+result); //"100"
/*
* 查看当前valueOf方法的源码
* public static String valueOf(int i) {
return Integer.toString(i); //Integer 类汇总的public static String toString(int i)---静态功能
}
* */
}
}
替换功能
替换功能
String replace(char oldChar, char newChar)
比较功能
public int compareTo(String anotherString)按字典顺序比较两个字符串
String类的compareTo(String anotherString)按照字典顺序比较,如何计算的
两个字符串进行比较(字典顺序)
s1,s2
1)将s1,s2分别转换成字符数组,获取这个两个长度:len1,len2
2)获取两个字符数组中长度的最小值 通过Math.min(len1,len2) ;----int lim
3)分别将字符数组的元素进行初始化:
char v1[] = {'元素1','元素2'...};
char v2[] = {'元素1','元素2'....} ;
4)定义统计变量k
while(k<lim){ //k 从0开始
//获取第一个字符数组中的第一个元素
char c1 = v1[k] = v1[0] = '元素1';
char c2 = v2[k] = v2[0] = '元素1' ;
//c1和c2相等,继续判断,继续判断第二个字符是否一致,依次这样,
(如果都一致,条件不成立)while循环结束
if(c1!=c2){
return c1 -c2 ;//字符对应的ASCII码表的值相减
}
k++;
}
5)return len1-len2;
其他功能
去除两端空格
public String trim()
面试题
1.数组中有没有length(),字符串中有没有length(),集合中有没有length()方法?
数组中没有length方法的,获取数组长度 :length属性
arr.length;
String类中:有length() :获取字符串中的字符的长度
集合中:没有length() : 获取集合的元素数:size()
2.String s = "hello" 和 String s = new String("hello") 分别表示什么?分别创建了几个对象?
都是表示创建字符串对象,里面内容为hello
开发中书写字符串对象---String 变量名 = 值;
前者:在常量池中开辟空间地址,创建一个对象---直接在常量池中操作
后者:在格式上,是在堆内存中创建空间的,并且指向的常量标记"hello"
两个对象:堆内存中要new,另一个还要指向常量标记---常量池的空间地址
从内存角度考虑:使用前者优于后者,后者开销资源大,浪费内存空间!
3.看程序,写结果
public class StringDemo2 {
public static void main(String[] args) {
String s1 = "hello" ;
String s2 = new String("hello");
System.out.println(s1==s2);//false
System.out.println(s1.equals(s2));//true
System.out.println("------------------------");
String s3 = "hello" ;
String s4 = "hello" ;
System.out.println(s3==s4);//true
System.out.println(s3.equals(s4));//true
System.out.println("------------------------");
String s5 = new String("hello") ;
String s6 = new String("hello") ;
System.out.println(s5==s6);//false
System.out.println(s5.equals(s6));//true
}
}
4.看程序,写结果
==和equlas的区别:
字符串常量相加和字符串变量相加的区别
字符串常量相加---先相加,然后在常量池中找是否存在这个常量,如果存在,直接返回常量池中的该地址值;
如果不存在,在常量池中开辟空间
字符串变量相加----先开辟空间,然后对结果----拼接----新的字符串变量(== 考虑其地址值)
public class StringDemo3 {
public static void main(String[] args) {
String s1 = "hello" ;
String s2 = "world" ;
String s3 = "helloworld" ;
System.out.println(s3 == (s1+s2));//false
System.out.println(s3.equals(s1+s2))//true
System.out.println(s3 == "hello"+"world") ;//true
System.out.println(s3.equals("hello"+"world")) ;//true
}
}
5. 面试题
String s1 = "hello" ;
String s2 = "hel" ;
这两个字符串按照字典顺序比较的时候,值是多少呢?
public class StringTest3 {
public static void main(String[] args) {
String s1 = "hello" ;
String s2 = "hel" ;
String s3 = "abc" ;
String s4 = "hsbc" ;
System.out.println(s1.compareTo(s2));//5-3 = 2
System.out.println("-----------------------");
System.out.println(s1.compareTo(s3));//7
System.out.println(s1.compareTo(s4));//-14
}
}
编码和解码
/**
*
* 编码和解码的使用 ------ 后期IO流会用到!
*
* 中文存储的时候:utf-8:一个中文对应三个字节
* gbk:两个字节
*
* 里面第一个字节一定是负数!
编码:String----byte[] 字节数组
getBytes()--->byte[]
解码:byte[]----String:
String类的构造方法:
String(byte[] bytes)
*
*/
public class StringDemo {
public static void main(String[] args) throws UnsupportedEncodingException {
//定义一个String
// String s = "abc" ;
String s = "我爱中国" ;
//编码:String---byte[]
// public byte[] getBytes():默认平台编码集 进行转换 (UTF-8)
// byte[] bytes = s.getBytes("GBK");
byte[] bytes = s.getBytes();
// System.out.println(bytes);
//Java中提供了一个工具类:Arrays
//里面的功能都是静态的功能:public static String toString(byte[] a):将数组转换成字符串 : 字符数组中的:ASCII码表中的值
System.out.println(Arrays.toString(bytes)); //[97,98,99]
//[-26, -120, -111, -25, -120, -79, -28, -72, -83, -27, -101, -67] :utf-8格式
//[-50, -46, -80, -82, -42, -48, -71, -6]:gbk格式
System.out.println("--------------------------------");
//解码
// String(byte[] bytes) :通过字节数组构成成字符串成
// String result = new String(bytes,"GBK") ; //gbk解码
String result = new String(bytes) ; //utf-8解码
System.out.println(result);
}
}
可变长字符串类
StringBuffer:可变长字符串 JDK1.0提供,运行效率慢,线程安全
StringBuilder:可变长字符串,JDK5.0提供,运行效率快,线程不安全
StringBuffer类
线程安全的可变字符序列------ 字符串缓冲区
线程安全---->意味着 “同步操作” ------ >执行效率低
线程不安全----->意味着 “不同步” -------执行效率高
构造方法
StringBuffer() :无参构造方法 :构造一个其中不带字符的字符串缓冲区,其初始容量为 16 个字符。
public StringBuffer(int capacity):构造一个字符串缓冲区,指定容量大小
public StringBuffer(String str):构造一个带内容的字符串缓冲区:字符串的初始容量为 16 加上字符串参数的长度
public int capacity():获取容量大小
public int length():获取字符串长度
添加功能
public StringBuffer append(xxx);将任意类型的内容追加到此序列中(字符串缓冲区中)
public StringBuffer insert(int offset,String xxx):在指定位置处插入指定的元素
删除功能
public StringBuffer deleteCharAt(int index):删除指定位置处的字符序列,返回值是StringBuffer本身
public StringBuffer delete(int start,int end):从指定位置删除到指定位置结束的字符序列(包含start位置,结束到end-1),返回值自己本身
替换功能
public StringBuffer replace(int start,int end,String str)
从指定位置处开始到end-1处,使用str字符进行替换,返回StringBuffer本身
反转功能
public StringBuffer reverse():将缓冲区中的字符串进行反转,返回的StringBuffer本身
截取功能
public String substring(int start):从指定位置开始截取,默认截取到末尾,返回值是新的字符串
public String substring(int start,int end):从指定位置开始截取到end-1的位置,返回值是新的字符串
String和StringBuffer之间的相互转换
String----StringBuffer
方式1:
StringBuffer的有参构造方法
StringBuffer(String str)
方式2:
StringBuffer的无参构造方法+添加功能:append(xx)
StringBuffer sb = new StringBuffer() ;
sb.append(指定的字符串) ;
StringBuffer---String
StringBuffer buffer = new StringBuffer("javaee");
//方式1:利用String类型的有参 构造方法
String(StringBuffer buffer)
String s = new String(buffer) ;
//方式2:
StringBuffer的toString的方法进行转换
String s2 = buffer.toString() ;
String与StringBuffer作为方法形参的区别
String作为形式参数(是一种特殊引用类型)的 特点:
形式参数的改变不会直接影响实际参数,它和基本数据类型作为参数是一样的!
StringBuffer作为形式的特点:
形式参数改变会直接影响实际参数,和引用类型作为形式参数一样的
String与StringBuffer的区别
String:字符串是常量,一旦被赋值,其值不能被更改
String作为形式参数,形参的改变不会影响实际参数,它和基本类型作为形式参数效果是一样的
StringBuffer:线程安全的可变字符序列
线程安全的类----->同步的----执行效率低,
单线程程序中(只考虑执行效率)使用StringBuilder替代StringBuffer
StringBuffer作为形式参数,形参的改变会直接影响实际参数!
StringBuffer和数组的区别
两个共同点:都是"容器"
数组:只能同一种数据类型的元素, 数据类型必须一致的!
数据类型:既可以存储基本数据类型,也可以存储引用数据类型
StringBuffer: 字符串缓冲区 --->存储的可变的字符序列,
存储是字符串数据(字符串本身由很多个字符组成的)
线程安全的类----同步的----执行效率低 (单线程程序中,使用StringBuilder去替代StringBuffer)
StringBuffer和StringBuilder的区别
两个都是字符串缓冲区
StringBuffer: 线程安全的类---同步的-----执行效率低
StringBuffer和StringBuilder具有相互兼容的api
StringBuffer里面的所有的成员方法几乎都有一个关键字:synchronized:同步锁!(互斥锁)
StringBuilder:线程不安全的类---不同步的---执行效率高
单线程中:StringBuilder去替代了StringBuffer,多线程环境中,优先使用的StringBuffer!
题目
指定一个数组,静态初始化了,将int类型数组,拼接成字符串数据
import java.util.Arrays;
/**
* 需求:
* 指定一个数组,静态初始化了,将int类型数组,拼接成字符串数据
* [元素1, 元素2, 元素3]
* int[] arr = {11,22,33,44,55} ;
* 使用功能改进: (两种方式)
* 结果: String类型
*
*/
public class StringTest {
public static void main(String[] args) {
//创建一个数组,静态初始化
int[] arr = {11,22,33,44,55} ;
String result = arraytoString(arr);
System.out.println("result:"+result);
System.out.println("-------------------------------");
//如果题意没有要求自己使用功能,那么直接使用Arrays工具类toString//方式2
String result2 = Arrays.toString(arr);
System.out.println("result2:"+result2);
System.out.println("-------------------------------");
//利用StringBuffer的append的追加
String result3 = arrayToString2(arr);
System.out.println("result3:"+result3);
}
//方式3
public static String arrayToString2(int[] arr){
//创建一个字符串缓冲区
StringBuffer sb = new StringBuffer() ;
sb.append("[") ;
//遍历数组
for(int x =0 ; x < arr.length ; x ++){
//判断
if(x == arr.length-1){
sb.append(arr[x]).append("]") ;
}else{
sb.append(arr[x]).append(", ") ;
}
}
return sb.toString() ;
}
//ctlr+alt+m:抽取方法
//使用功能改进
public static String arraytoString(int[] arr) {
//定义一个字符串:空字符串
String result = "" ;
//拼接做中空号
// result.concat("[") ; //拼接功能
result += "[" ;
//遍历数组
for(int x = 0 ; x < arr.length ; x ++){
//判断是否x是最大索引值
if(x == arr.length-1){
result += arr[x] ;
result += "]" ;
}else{
result += arr[x] ;
result +=", " ;
}
}
return result;
}
}
键盘录入字符串,将字符串反转,使用功能改进
import java.util.Scanner;
/**
* 需求:键盘录入字符串,将字符串反转,使用功能改进
*/
public class StringTest2 {
public static void main(String[] args) {
//创建键盘录入对象
Scanner sc = new Scanner(System.in) ;
//提示并录入数据
System.out.println("请您录入一个字符串数据:");
String line = sc.nextLine() ;
//调用
String result = reverse(line);
System.out.println(result);
System.out.println("------------------------------");
String result2 = reverse2(line);
System.out.println(result2);
}
//方式2:利用StringBuffer的反转功能
public static String reverse2(String s){
//分别走:创建一个字符串缓冲区对象
/*StringBuffer sb = new StringBuffer() ;
//追加
sb.append(s) ;
//反转
sb.reverse() ;
return sb.toString() ;*/
//一步走
return new StringBuffer().append(s).reverse().toString() ;
}
//方式1:将字符串转换成字符数组
public static String reverse(String s){
//将字符串转换成字符数组
char[] chs = s.toCharArray();
//定义一个空串
String result = "" ;
//遍历字符数组
for(int x = chs.length-1; x >=0 ; x --){
result+=chs[x] ;
}
return result ;
}
}
//判断是否x是最大索引值
if(x == arr.length-1){
result += arr[x] ;
result += "]" ;
}else{
result += arr[x] ;
result +=", " ;
}
}
return result;
}
}
###### 键盘录入字符串,将字符串反转,使用功能改进
```java
import java.util.Scanner;
/**
* 需求:键盘录入字符串,将字符串反转,使用功能改进
*/
public class StringTest2 {
public static void main(String[] args) {
//创建键盘录入对象
Scanner sc = new Scanner(System.in) ;
//提示并录入数据
System.out.println("请您录入一个字符串数据:");
String line = sc.nextLine() ;
//调用
String result = reverse(line);
System.out.println(result);
System.out.println("------------------------------");
String result2 = reverse2(line);
System.out.println(result2);
}
//方式2:利用StringBuffer的反转功能
public static String reverse2(String s){
//分别走:创建一个字符串缓冲区对象
/*StringBuffer sb = new StringBuffer() ;
//追加
sb.append(s) ;
//反转
sb.reverse() ;
return sb.toString() ;*/
//一步走
return new StringBuffer().append(s).reverse().toString() ;
}
//方式1:将字符串转换成字符数组
public static String reverse(String s){
//将字符串转换成字符数组
char[] chs = s.toCharArray();
//定义一个空串
String result = "" ;
//遍历字符数组
for(int x = chs.length-1; x >=0 ; x --){
result+=chs[x] ;
}
return result ;
}
}