05、面向对象语法

228 阅读46分钟

一、面向对象思想

①面向过程

[1]面向过程程序特点

在顺序、分支、循环结构中,操作变量中存储的数据。

images

[2]面向过程思想对应的问题

面向过程这种程序设计理念,对应现实生活中某一个点上具体的问题,特别是对应解决问题过程中具体的操作步骤。

[3]面向过程程序的不足

最大的不足:整个系统各个组成部分之间『耦合』太严重。

  • 从开发角度来说:对任何一个细微的地方做修改、调整、升级都会不可避免的影响到其他部分。
  • 从运行角度来说:整个系统中有任何一个数据发生了变化,相关的很多部分都要重新运算,非常浪费性能。

[4]本质

从面向过程的思想发展到面向对象的思想根本原因就是因为随着时代、市场等方方面面的发展,要求程序员编写更加复杂的程序,设计更加庞大的系统。

由非常多组成部分有机结合在一起的系统是面向过程代码无法容纳的。

②面向对象

[1]关键

面向对象的设计理念、编程思想非常贴合人平时认识世界、思考问题时的思维方式。

一切皆对象:现实世界中的所谓『东西』投射到代码中就是『对象』

  • 工业产品:一辆汽车中的各个组成部分、各个部件中各种的零件
  • 日常生活:做饭时使用的厨具、食材、餐具等等“东西”

所以有了面向对象的设计理念和编程思想,我们就可以在开发过程中,大致保持和平时一样的思维方式来设计系统、编写代码。

[2]类

程序中的『类』,对应到生活中就是对同一类事物的一个统一的『分类名称』。

images

[3]类和对象的关系

  • 从抽象和具体的角度来说:

    • 类:抽象描述
    • 对象:具体实体,所以对象也被称作:『类的实例』
  • 从设计图和产品的角度来说:

    • 类:生产产品的设计图
    • 对象:按照设计图生产出来的一个具体的产品

使用汽车设计图可以生产汽车产品。

使用汽车类可以创建汽车对象。

images

二、创建类和对象

①类的组成部分

[1]属性(对应程序中的数据)

通过程序中的各种数据类型,来描述一个类的『特征』,例如:汽车的颜色、汽车的排量、汽车的价格……

[2]行为(对应程序中的操作)

通过程序中的方法(也就是面向过程视角中的函数),来执行这个类的对象能够做的『动作』。例如:汽车的行驶、汽车的鸣笛、汽车的启动、汽车的停泊……

 属性行为
毛色 品种 年龄 体重 性别吃猫粮 抓老鼠 走猫步 撒娇 喵喵
品种 年龄 高度 功用生长 落叶 结果 枯萎
英文名面向过程角度名称面向对象角度名称
Filed成员变量属性
Method成员函数方法

②声明一个类(空)

images

③声明一个类(有属性、方法)

 public class Person {
 ​
     // 属性/成员变量
     // 暂时先不管声明属性的具体规则,像以前一样声明变量先使用
     // 这个成员变量即使没有初始化,系统也会为它分配初始值
     // int类型的初始值是0
     int weight;
 ​
     // 方法/成员函数
     // 暂时先不管声明函数的具体规则,先声明一个最简单的
     // public:表示这个方法可以在任何地方被调用
     // void:表示这个方法没有返回值
     // (int food):声明这个方法在被调用时可以传入的参数
     public void eat(int food) {
         weight = weight + food;
     }
 ​
     public void say() {
         System.out.println("我现在的体重是:" + weight);
     }
 ​
 }

④声明一个对Person进行测试的类

 public class PersonTest {
 ​
     public static void main(String[] args) {
 ​
         // 1.创建Person类的对象
         // 第一步:声明一个Person类这个类型的变量
         // 第二步:使用new关键字创建Person类的对象,对象赋值给第一步声明的变量
         //          在new关键字后面的,是Person类的『构造器』
         //          构造器在以后还会介绍更多细节,现在大家就按照:类名()的格式写就行
         Person person = new Person();
 ​
         // 2.调用Person对象的eat()方法:让这个人吃饭
         // 调用有参数方法的格式:指向对象的那个变量.方法名(参数)
         person.eat(5);
 ​
         // 3.调用Person对象的say()方法:让这个人说说他的体重
         // 调用无参数方法的格式:指向对象的那个变量.方法名()
         person.say();
 ​
         // 4.直接修改weight属性
         // 在类之外的其他地方是否可以操作类的属性,取决于属性的修饰符
         // 在将来学习了面向对象的『封装』特性后,我们会知道,在类的外部不建议直接操作类的属性
         person.weight = 20;
 ​
         person.say();
     }
 ​
 }

三、包

images

①包的作用

[1]区分同名的类

包最重要的作用:区分同名的类,例如:

  • JDK自带一个Book类:

images

  • 将来我们做书城项目也会创建Book类

如果没有包名,两个Book类就无法区分了。

②简单类名和全类名

  • 简单类名:不带包名的类名
  • 全类名:包名.类名

全类名举例:

com.zn.bookstore.entity.Book

java.awt.print.Book

四、类中的属性

①声明属性的语法格式

修饰符 数据类型 属性名;

修饰符 数据类型 属性名 = 初始化值;

②初始化值

声明一个属性时,如果没有明确的对这个属性进行初始化值的设置,那么系统会给这个属性设置默认值。所以如果有确定的需要可以设置属性的默认值,如果没有特殊需要,不设置也行。

images

③数据类型

  • 基本数据类型
  • 引用数据类型

④权限修饰符

权限修饰符不仅对类的属性有要求,对类中的方法的要求是完全一样的:

修饰符名称含义本类同包其他类子类同工程其他类
private私有×××
default缺省××
protected受保护×
public公共
  • 同包类访问私有属性:

images

  • 同工程、不同包的类访问缺省权限属性:

images

  • 同工程、不同包的类访问受保护权限属性:

images

⑤属性名

属性也叫成员变量,所以属性名作为标识符适用变量的标识符规则和规范。

images

⑥构造器

构造器有什么用

  • 创建对象
  • 初始化对象的信息
 class Animal{
     
     String name; //姓名
     private int age; //年龄
     private int legs;//个数
     
     //构造器
     public Animal() {
         super();
         // TODO Auto-generated constructor stub
     }
 }

注意:Java语言中,每个类都至少有一个构造器

         默认构造器的修饰符与所属类的修饰符一致
 ​
         一旦显式定义了构造器,则系统不再提供默认构造器
 ​
         一个类可以创建多个重载的构造器
 ​
         父类的构造器不可被子类继承

五、对象的内存分析

 public class Cat {
 ​
     private String catName = "dingdang";
     int weight;
 ​
     public void say(String master) {
 ​
         boolean emotion = true;
 ​
         if (emotion) {
 ​
             int food = 5;
 ​
             System.out.println("My name is " + catName + ",I want food:" + food);
 ​
         }
 ​
     }
 ​
 }

①基本情况

[1]声明变量

 // 1.声明Cat类型的变量
 Cat cat;

images

[2]创建对象

大家可以记住:只要看见使用了new关键词,就表示在堆内存中一定开辟了新的空间,一定创建了新的对象。

         // 2.给Cat类型的变量赋值一个Cat类型的对象
         cat = new Cat();

images

[3]调用方法

         // 3.调用Cat对象的方法
         cat.say("tom");

images

[4]say()方法执行完成

局部变量就会被释放掉

images

[5]main()方法执行完成

main()方法中的cat局部变量也会被释放掉。然后Cat对象就成了“断了线的风筝”被垃圾回收机制回收。

images

②创建两个对象

证明:两个对象在堆空间中分别有自己的副本,分别进行操作时互不影响。

[1]创建第一个对象

Cat cat01 = new Cat();

images

[2]创建第二个对象

Cat cat02 = new Cat();

images

[3]第一个变量设置属性

cat01.weight = 10;

images

[4]第二个变量设置属性

cat02.weight = 20;

images

③两个变量指向同一对象

        Cat cat03 = new Cat();
        Cat cat04 = cat03;

images

分别用两个变量修改weight属性:

        cat03.weight = 66;

images

cat04.weight = 88;

images

④变量置空

[1]将cat04置空

cat04 = null;

images

[2]将cat03置空

cat03 = null;

images

六、方法

①概念

[1]代码复用

images

[2]参数和返回值

方法的参数和返回值都不是必须有的,根据需要设置。

images

  • 方法的参数:我们命令方法替我们干活的时候,有时需要给方法提供一些数据。

    • 形式参数:声明方法时指定的参数,用来说明这个方法可以接受哪些参数(简称形参)
    • 实际参数:调用方法时实际传入的参数,用于方法执行过程中实际的运算(简称实参)
  • 方法的返回值:方法执行完成后,给方法的调用者返回的结果数据。

②方法的声明和调用

洗衣机方法
工厂生产洗衣机声明方法
用户使用洗衣机调用方法

[1]声明方法的代码

// 声明一个方法:求两个整数的和
// int a,int b声明方法的参数:形式参数
// public int:表示方法的访问权限是public,返回值是int类型
// 方法名:add
public int add(int a, int b) {

    // 计算a + b的和
    int addResult = a + b;

    // 使用return关键字将a + b的和返回给方法的调用者
    return addResult;
}

[2]调用方法的代码

// 1.要调用一个方法,需要先创建方法所在类的对象
Calculator calculator = new Calculator();

// 2.通过对象调用方法
// 这里传入给方法的5、11,我们叫做实际参数
int addResult = calculator.add(5, 11);

③方法相关规则

  • 方法调用一次就会执行一次
  • 方法必须写在类中,不能独立存在。
  • 方法如果没有返回值,方法体内可以没有return;如果写了return,那么表示方法结束。
  • 方法声明的返回值类型必须和实际返回的数据类型一致。
  • 方法内部不能再声明方法。

④递归方法

	//已知有一个数列:f(0) = 1,f(1) = 4,f(n+2)=2*f(n+1) + f(n),
	//其中n是大于0的整数,求f(10)的值。
	public int f(int n){
		if(n == 0){
			return 1;
		}else if(n == 1){
			return 4;
		}else{
	//return f(n + 2) - 2 * f(n + 1);
			return 2*f(n - 1) + f(n - 2);
		}
	}

输出:10497

七、面向对象特征

①封装

为什么要有封装

程序设计追求 高内聚 ,低耦合

高内聚:类的内部数据操作细节自己完成,不允许外部干涉

低耦合:仅对外暴露少量的方法用于使用

隐藏对象内部的复杂性,只对外公开简单的接口。把该隐藏的隐藏起来,该暴露的暴露出来。这就是封装性的设计思想

封装代码示例

class Animal{
	
	String name; //姓名
	private int age; //年龄
	private int legs;//个数
	
	//构造器
	public Animal() {
		super();
		// TODO Auto-generated constructor stub
	}
	
	//有参构造器
	public Animal(String name, int age, int legs) {
		super();
		this.name = name;
		this.age = age;
		this.legs = legs;
	}

	//setter和getter方法
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	public int getLegs() {
		return legs;
	}
	public void setLegs(int legs) {
		this.legs = legs;
	}
	
	
	@Override
	public String toString() {
		return "Animal [name=" + name + ", age=" + age + ", legs=" + legs + "]";
	}
	
}

通过以上java代码将数据声明为private(私有),再提供公共的public方法:getter和setter对属性操作

问题:java这样做的意义是什么

  • 隐藏一个类中不想要外部因素知道的信息
  • 访问者只能通过开始定制好的方法来访问数据,防止访问者胡乱更改数据
  • 便于修改,增强代码的可维护性

②继承

1.1 生活中的继承

生活中,子继承父的遗产,不是全部,只能是部分

1.2 程序中的继承

在程序中,也能出现继承的关系,让一个类去继承另一个类.

比如 A类继承B类

  • 出现继承的关系
  • A类是B类的子类,或者称为派生类
  • B类是A类的父类,或者称为超类,基类
  • 子类可以直接拥有父类的成员(不是全部)

1.3 继承的语法格式

继承使用关键字 extends,表示继承的意思

注意: extend 本身含义,扩展,延伸

  • 定义格式

    class B{}
    class A extends B{}  // A 继承 B类
    
  • 继承的入门案例 -- 子类拥有父类成员

    /**
     *  定义的是 Teacher和Manager类的共性内容
     *  姓名年龄
     */
    public class Person {
        String name ;
        int age;
    }
    
    /**
     *  管理类,班主任类
     */
    public class Manager extends Person{
    
    }
    
    /**
     *  老师类
     *  重复,只做一次
     *  想法:
     *    String name;
     *     int age;
     *  换一个地方定义(共性抽取)
     *  Teacher和Manager共用
     *  使用Person中的成员
     *  Teacher继承Person
     */
    public class Teacher extends Person{
    
    }
    
        public static void main(String[] args) {
            /**
             *  创建对象, Person的子类对象
             *  Person : Teacher,Manager
             */
            Teacher teacher = new Teacher();
            //teacher对象,调用成员变量
            //teacher子类对象调用继承的成员变量
            teacher.name = "张三";
            teacher.age = 20;
    
            Manager manager = new Manager();
            manager.name = "李四";
            manager.age = 22;
    
            System.out.println("teacher.name = " + teacher.name);
            System.out.println("teacher.age = " + teacher.age);
    
            System.out.println("manager.name = " + manager.name);
            System.out.println("manager.age = " + manager.age);
        }
    

1.4 继承的好处

上面案例,使用程序中的继承,使用继承的好处:

  • 减少代码量

  • 复用性提高

  • 继承的存在,导致了面向对象的最后一个特征多态

    继承有弊端: 类和类之间的紧密性更强.(扩展性越差)

2. 继承后成员特点

2.1 继承后成员变量特点

子类和父类中的成员变量同名

  • 调用的使用: 子类自己有,使用自己的,子类没有,使用父类

    public class Fu{
        String s = "父类";
    }
    
    public class Zi extends Fu{
        String s = "子类";
    }
    
    public static void main(String[] args) {
        //创建对象,子类对象
        Zi zi = new Zi();
        //子类对象,调用成员s
        System.out.println(zi.s);
    }
    
2.2 super关键字

super关键字是超级的意思,在子类中调用父类的成员,使用此关键字

super.变量  调用父的成员变量
super.方法() 调用的是父类的成员方法

this表示当前对象,super表示父类在内存中的存储空间,不是对象

public  class Fu{
    String s = "父类";
}
public class Zi extends Fu {
    String s = "子类";

    public void print(){
        String s = "方法";
        System.out.println(s);
        System.out.println(this.s);
        System.out.println(super.s);
    }
}
public static void main(String[] args) {
    //创建子类对象,调用方法
    Zi zi =  new Zi();
    zi.print();
}
2.3 继承后成员方法特点

方法重写override: 子类父类出现了一模一样的方法,称为子类重写了父类的方法.又称为覆盖或者复写.

调用子类重写的方法,假如子类没有重写,调用父类的方法

public class Fu {
    public void print(){
        System.out.println("父类方法print");
    }
}
public class Zi extends Fu {
    /**
     * 要重写父类的方法
     * 直接复制
     */
    public void print(){
        System.out.println("子类方法print::重写");
    }
}
    public static void main(String[] args) {
        //创建子类对象
        Zi zi = new Zi();
        zi.print();
    }
2.4 方法重写的意义

继承本质是扩展的意思,延伸的意思,依靠方法的重写来实现

方法重写引入案例,理解重写的意义

/**
 *  定义手机类
 *  早年代手机
 */
public class Phone {
    /**
     * 定义手机的来电显示功能
     * 数字移动电话
     */
    public void showingCall(){
        System.out.println("来电显示号码");
    }
}

/**
 *  新手机:
 *  功能变强,但是原有功能继续复用
 *  继承和方法重写的思想
 */
public class Iphone extends Phone {
    //重写父的方法
    //方法名字不变,用户不需要重新知
    public void showingCall(){
        //复用显示号码功能,父类的方法中,已经完成了
        //调用父类的方法
        super.showingCall();
        //新增的功能
        System.out.println("显示大头像");
        System.out.println("归属地");
        System.out.println("意思推销");
    }
}
public static void main(String[] args) {
    //创建手机对象
    Phone phone = new Phone();
    phone.showingCall();
    System.out.println("==========");
    //创建新手机对象
    Iphone iphone = new Iphone();
    iphone.showingCall();
}
2.5 方法重写小问题

方法重写需要考虑权限的.保证子类方法的权限要大于或者等于父类方法权限

  • 父类的方法权限是public

    class Fu{
        public void print(){}
    }
    class Zi extends Fu{
        public void print(){} //正确
        protected void print(){} //错误,权限降低
        void print(){} //错误,权限降低
        private void print(){} //错误,权限降低
    }
    
  • 父类的方法权限是protected

    class Fu{
        protected void print(){}
    }
    
    class Zi extends Fu{
        public void print(){} //正确
        protected void print(){} //正确
        void print(){} //错误,权限降低
        private void print(){} //错误,权限降低
    }
    
  • 父类方法权限是默认

    class Fu{
         void print(){}
    }
    
    class Zi extends Fu{
        public void print(){} //正确
        protected void print(){} //正确
        void print(){} //正确
        private void print(){} //错误,权限降低
    }
    
  • 如果父类的方法权限是private, 子类不知道该方法的存在,没有继承的说法

2.6 继承后构造方法特点

构造方法特点: 子类的构造方法中,第一行存在隐式代码 (写不写都存在),代码是super(); 调用父类的无参数构造方法.

public class Fu  {
    public Fu(){
        System.out.println("父类构造方法");
    }
}
public class Zi extends Fu {
    public Zi(){
        super(); //调用父类的无参数构造方法.
        System.out.println("子类构造方法");
	}
}
  • 子类的构造方法,无论重载多少个,第一行肯定也是super();
public class Zi extends Fu {
    public Zi(){
        super(); //调用父类的无参数构造方法.
        System.out.println("子类构造方法");
    }

    public Zi(int a){
        super(); //调用父类的无参数构造方法.
        System.out.println("子类构造方法::重载的");
    }
}
  • 父类没有无参数构造方法,子类的构造方法中,super(传递参数)
  • 父类中存在多个构造方法,子类的构造方法只要调用到其中的一个即可
public class Fu  {
    /**
     * 父类的构造方法
     * 加上参数,有参数的构造
     */
    public Fu(int a){
        System.out.println("父类构造方法" + a);
    }

    public Fu(String s){
        System.out.println("父类构造方法" + s);
    }
}

public class Zi extends Fu {

    public Zi(){
        //调用父类的无参数构造方法
        //父类中没有无参数构造
        //传递响应的参数
        super(7);
    }

    public Zi(String s){
        //调用父类构造方法,保证调用其中一个
        super("字符串");
    }
}

3. 继承特点

3.1 单继承

一个类只能继承一个类,不允许同时继承多个类

class A extends B,C{} //不允许的行为

单继承存在局限性,解决局限性问题,接口的概念

3.2 多层继承
class A extends B{}
class B extends C{}
//多层继承,允许实现
//class C extends Object{}

A类可以同是拥有B和C的成员, B只能拥有C的成员

A类中super调用的是B类成员,如果B类没有成员,调用C成员

Object类是java中的皇帝,所有的类都是Object子类

3.3 继承体系

③多态

  • 学习目标

    • 对象的多态性
    • 多态的必要因素
    • 多态的语法格式
    • 多态中成员的特性
    • 多态中的转型
    • 抽象类定义
    • 抽象方法定义
    • 抽象类使用
    • 抽象类的成员特点
    • 抽象类的多态调用
    • 抽象类的继承体系价值

1. 对象的多态性

引入 : 生活中的多态性! 你自己的身份是学生,你的身份职场精英,患者.在不同的时期不同环境,状态是不同的.

生活中的多态性: 一个事物具备的不同形态.

1.1 对象多态性前提

  • 必须有继承或者是接口实现
  • 必须有方法的重写

多态的语法规则: 父类或者接口的引用指向自己的子类的对象

父类 变量(对象名)  =  new 子类对象(); //多态写法

对象调用方法,执行的子类的方法重写

1.2 多态中成员的特点

  • 多态中成员变量的特点

    • 编译 : 父类中没有成员变量,编译失败
    • 运行 : 运行父类中的成员变量
  • 多态中成员方法的特点

    • 编译 : 父类中没有成员方法,编译失败
    • 运行 : 运行子类的方法重写
  • 简练 : 成员方法编译看左边,运行看右边.成员变量都是左边

    Person p = new Student();

public class Person {
   String s = "父类成员";

   public void eat(){
       System.out.println("人在吃饭");
   }
}
public class Student extends Person {
    String s = "子类成员";

    public void eat(){
        System.out.println("学生吃饭");
    }
}
    public static void main(String[] args) {
        Person p = new Student();
        //对象p,子类对象,调用成员变量s
        System.out.println(p.s);
        //子类对象调用方法
        p.eat();
    }

1.3 多态的转型

多态的程序中,不能调用子类的特有成员!!

只能调用子类父类的共有成员!!

转后类型 变量名 = (转后类型)要转的数据; //公式
 public static void main(String[] args) {
     //创建对象,多态性
     //父类 = new 任意子类对象() 扩展
     Animal animal = new Cat();
     animal.eat();
     //Cat类的特有功能 catchMouse()方法
     //类型转换,强制
     //Cat提升为了Animal,转回Cat类型
     Cat c = (Cat)animal;
     c.catchMouse();
 }

补充: 多态性提升扩展性,是否需要强制转换,根据实际功能需求来决定.

1.4 多态中的转型异常

异常ClassCastException 类型转换异常,在多态中经常发生.

是在进行类型的强制转换的时候发生,我们现在的案例是Dog不能转成Cat.

需要解决这个异常 : 对象是Cat转Cat,是Dog换Dog

运算符 : 比较运算符,结果是boolean类型

运算符是关键字 instanceof

instanceof的语法格式:

对象名 instanceof  类的名字
解析: 比较这个对象,是不是由这个类产生的
c instanceof Cat  解释: c对象是不是Cat类产生的,如果是结果就是true

强制类型转换之前的安全性判断

public static void main(String[] args) {
    //多态创建对象
    Animal animal = new Dog();
    animal.eat();
    //判断 animal是不是Cat类的对象
    //boolean b = animal instanceof Dog ;
    //System.out.println(b);
    //调用子类的特有方法
    if (animal instanceof Cat){
        //if为true,强制转换为Cat
        Cat c = (Cat)animal;
        c.catchMouse();
    }
    if (animal instanceof Dog){
        Dog d = (Dog)animal;
        d.lookHome();
    }
}

1.5 多态的转型案例

public static void main(String[] args) {
    //创建对象,多态
    Person p1 = new Faculty();
    //p1对象的属性,赋值本科 degree 子类的特有成员
    //判断p1对象是不是Faculty类产生
    if (p1 instanceof  Faculty){
        Faculty f = (Faculty)p1;
        f.setDegree("本科");
        System.out.println(f.getDegree());
    }
    Person p2 = new Staff();
    //判断p2对象是不是Staff类产生
    if (p2 instanceof  Staff){
        Staff s = (Staff)p2;
        s.setDuty("职员");
        System.out.println( s.getDuty());
    }
}

2. 抽象类 abstract

抽象的概念 : 凡是说不清楚的都是抽象

例子 : 我买了一台手机,买了一直笔,都是抽象概念.

具体: 华为Meta40Pro ,金属, 16G+512

程序中 : 我知道这个功能存在,但是怎么完成就说不清楚,程序中也出现了抽象.

2.1 抽象方法定义

使用关键字 abstract定义抽象方法

权限修饰符 abstract 返回值类型 方法名字(参数列表) ;
abstract关键字
抽象方法没有方法体, 不需要{},直接分号结束

当一个类中的方法是抽象方法的时候,这个类必须是抽象类,在类的关键字class前面使用abstract修饰.

public abstract class 类名{}
public abstract class Animal {
    /**
     * 动物吃什么?
     * 说不清楚,抽象,可以不说
     */
    public abstract void eat();
}

2.2 抽象类的使用方式

  • 抽象类不能实例化对象,不能new对象.

    • 为什么不能建立对象,类中有没有主体的方法存在,建立对象调用抽象方法是绝对的错误,因此不能建立对象.
  • 需要子类继承抽象类,重写抽象方法.

  • 创建子类对象

  • 使用多态性创建对象,调用方法执行子类的重写

public class Cat extends Animal{

    /**
     * 重写父类的方法
     * 去掉修饰符 abstract
     * 添加主体 {}
     */
    public  void eat(){
        System.out.println("猫吃鱼");
    }
}
public static void main(String[] args) {
    //创建Animal的子类对象
    Animal animal = new Cat();
    //eat方法不可能执行父类,运行子类的重写
    animal.eat();
}

2.3 抽象类中成员的定义

2.3.1 抽象类中能否定义成员变量

可以定义成员变量,成员变量私有修饰,提供方法 get/set,由子类的对象使用

public abstract class Animal {
    //抽象类中能否定义成员变量
    private String name;
    public abstract void eat();

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
public static void main(String[] args) {
    Animal animal = new Cat();
    animal.eat();
    //animal对象调用方法 get/ set
    animal.setName("tom");
    String name = animal.getName();
    System.out.println(name);
}

2.3.2 抽象类中有构造方法吗

抽象类中有构造方法,不写有默认的

public abstract class Animal {

    public Animal(){
        System.out.println("Animal的构造方法");
    }

    public Animal(String name){
        this.name = name;
        System.out.println("有参数String的构造方法");
    }

    //抽象类中能否定义成员变量
    private String name;
    public abstract void eat();

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
public class Cat extends Animal {

    public Cat(){
        //调用父类的有参数构造方法
        super("张三");
    }

    @Override
    public void eat() {
        System.out.println("猫吃鱼");
    }
}

2.3.3 抽象中能否不定义抽象方法

抽象类中,可以不定义出抽象方法.

但是,如果有抽象方法存在,这个类必须是抽象类

2.4 子类还是抽象类的问题

当一个子类继承一个抽象类的时候,子类必须重写全部的抽象方法.假如子类重写了部分抽象方法,这个子类依然还是抽象类.

public abstract class Animal {
    public abstract void eat();
    public abstract void sleep();
}
/**
 * Cat继承父类Animal,Cat类拥有了父类的成员
 * 父类有什么,我就有什么
 */
public abstract class Cat extends Animal {
    public  void eat(){}
    /**
     * 方法sleep没有重写
     * 还是一个抽象的方法
     */
//    public abstract void sleep();
} 

2.5 员工案例

/**
 *  公司类
 *  定义的是所有员工的共性内容
 */
public abstract class Company {
    private String name; //员工姓名
    private String id; // 员工编号,唯一标识

    //工作行为,具体到某个岗位是不同,无法写出具体的工作内容
    public abstract void work();


    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }
}
/**
 *  研发部类
 */
public class Development extends Company{
    //重写工作的抽象方法
    //work方法中,输出自己的姓名和工号呢
    @Override
    public void work() {
        //调用父类的方法
        System.out.println(super.getName()+"::"+super.getId()+"研发部的员工在开发程序");
    }
}
/**
 *  定义财务部类
 */
public class Financial extends Company {
    @Override
    public void work() {
        System.out.println(super.getName()+"::"+super.getId()+"财务部员工在算账");
    }
  public static void main(String[] args) {
        //创建对象,子类对象,多态性
        Company c1 = new Development();
        //父类的方法,属性赋值
        c1.setName("张三");
        c1.setId("研发部001");
        //System.out.println(c1.getName() +"::"+c1.getId());
        c1.work();

        Company c2 = new Financial();
        c2.setName("李四");
        c2.setId("财务部001");
        c2.work();
    }

3. 接口 interface

3.1 接口无处不在

身边的接口有哪些,笔记本上USB接口,HDMI,TypeC接口,插座

USB接口 : 连接鼠标,键盘,摄像头,手机,移动硬盘,电风扇.设备的工作原理不同,但是都可以连接到USB接口上,完成他的任务.说明了一个问题 : 这些设备都满足USB的接口规范!!

接口:就是一个规范,或者称为标准 , 无论什么设备,只要符合接口标准,就可以正常使用.

接口的扩展性很强大.

3.2 Java中接口定义

当一个抽象类中的所有方法全部是抽象的时候,可以将这个抽象类换一个更加贴切的名词,叫他接口. 接口是特殊的抽象类.

定义接口,使用关键字 interface

语法规范:

public interface 接口名{}

接口在编译后,依然还是.class文件

3.3 接口中成员定义 (JDK1.7 版本)

  • 成员变量

    • 成员变量的定义是具有固定格式

    • 成员变量的修饰符是固定 public static final

      public static final 数据类型  变量名 = 值 ;
      
  • 成员方法

    • 成员方法的定义是具有固定格式

    • 成员方法的修饰符固定为 public abstract

      public abstract 返回值类型 方法名(参数列表) ;
      

3.4 接口的使用方式

  • 接口不能建立对象,不能new

  • 需要定义类,实现接口(继承类,在接口中称为实现,理解为继承)

    • 实现接口,使用新的关键字 implements
    • 实现的格式 class 类 implements 接口名{}
  • 重写接口中的抽象方法

  • 创建子类的对象

/**
 *  定义好的接口
 */
public interface MyInterFace {
    //接口的成员变量
    public static final int A = 1;
    //接口的成员方法
    public abstract void myInter();
}
/**
 *  定义MyInterFace接口的实现类
 *  重写接口的抽象方法
 */
public class MyInterFaceImpl implements MyInterFace{
   public void myInter(){
       System.out.println("实现类实现接口,重写方法");
    }
}
public static void main(String[] args) {
    //创建对象,多态性,创建接口实现类的对象
    MyInterFace my = new MyInterFaceImpl();
    my.myInter();
    //输出接口中的成员A的值
    System.out.println(my.A);
}

3.5 接口的多实现

类和类之间单继承,局限性的问题.接口的出现,是对单继承的改良,允许一个类同时实现多个接口.

语法格式:

class 类名 implements 接口A,接口B{}

实现类,重写实现的多有接口中的抽象方法

public interface A {
    public abstract void a();
}
public interface B {
    public abstract void b();
}
/**
 *  实现接口A和B
 */
public class C implements A,B{
    @Override
    public void a() {
        System.out.println("重写A接口方法");
    }

    @Override
    public void b() {
        System.out.println("重写B接口方法");
    }
}
public static void main(String[] args) {
    C c = new C();
    c.a();
    c.b();
}

八、接口

  • 学习目标

    • 接口的多继承
    • 实现类实现接口后,还是抽象类
    • 接口的规则体系案例
    • static静态修饰符
    • static内存表现
    • main方法的定义解析
    • 四大权限修饰符
    • final修饰符
    • 内部类
    • 匿名内部类

1. 接口

1.1 接口之间的关系

类和类之间是继承关系(单继承),类和接口之间是实现关系(多实现implements),接口和接口之间是继承关系,支持多继承,一个接口可以同时继承多个接口. interface A extends B,C,D{}

1.2 实现类还是抽象类

实现类实现接口,重写一部分抽象方法,实现类还是一个抽象类

public interface A {
    public abstract void a1();
    public abstract void a2();
}
public abstract class B implements A {
    public  void a1(){

    }
   // public abstract void a2();
}

1.3 接口规则案例

/**
 *  笔记本电脑类
 */
public class Computer {
    /**
     *  笔记本电脑,通过USB接口,使用外接设备
     *  方法定义,实现设备的使用
     *  返回值类型 : 这个方法经过运算后的结果的数据类型
     *  参数 : 其实方法的参数,是方法运算过程中的未知数据,才是参数
     *
     *  笔记本电脑,通过USB接口,使用外接设备 这个功能的未知数据,就是外接设备
     *  核心 : 未知设备,都有共同特性 : 满足接口规则
     *
     *  问题 : 这个方法调用 : 传递他什么
     *  参数是引用类,要传递对象,传递接口实现类对象
     */
    public void useUSB(USB usb){
//        USB usb =  new Mouse();
        //接口引用调用方法
        usb.start();
        usb.end();
    }
}
/**
 *  键盘类 : 满足接口标准
 */
public class KeyBoard implements USB{
    @Override
    public void start() {
        System.out.println("键盘工作");
    }

    @Override
    public void end() {
        System.out.println("键盘停止工作");
    }
/**
 *  鼠标类 : 接入USB接口工作
 *  满足接口的规则 : 程序中就是实现接口
 */
public class Mouse implements USB{
    @Override
    public void start() {
        System.out.println("鼠标打开开关,按键,滚轮");
    }

    @Override
    public void end() {
        System.out.println("开关关了");
    }
}
/**
 *  定义的USB接口 : 规则指定
 *  接口的规则 : 程序中的抽象方法
 */
public interface USB {
    //设备开始工作
    public abstract void start();
    //设备结束工作
    public abstract void end();
}
public class USBTest {
    public static void main(String[] args) {
        //测试笔记本案例
        //创建笔记本对象
        Computer computer = new Computer();
        //调用笔记本的方法 useUSB
       /* Mouse m = new Mouse();
        computer.useUSB(m);*/
        computer.useUSB( new Mouse() );

        //调用笔记本的方法,传递键盘对象 (USB接口实现类对象)
        computer.useUSB( new KeyBoard());
    }
}

2. 静态修饰符

static修饰符 : 最早出现在main方法中.只能修饰成员,不能写在方法的内部,被static修饰的成员,静态成员变量和静态的成员方法.

2.1 静态修饰成员变量

static 修饰的成员变量,是被所有的对象共享的数据.没有被static修饰的成员变量,是每个对象的独享数据或者是特有数据.

public class Person {
    String name;
    static String country = "中国";
}
public class StaticTest {
    public static void main(String[] args) {
        Person p1 = new Person();
        p1.name = "张三";
        Person p2 = new Person();
        p2.name = "李四";
        //使用对象p1修改变量country的值
        p1.country = "美国";
        System.out.println(p2.country);
    }
}

2.2 静态内存

  • 静态成员内存的特点

    • 静态成员跟随自己的类进入到元数据区(静态区域)
    • 静态成员属于自己的类,不属于对象
    • 静态成员进入内存后,赋默认值
    • 静态成员变量的初始化实际早于对象

2.3 静态成员的调用方式

静态的内存图中,已经很明白了,静态属于自己的类,不是对象,静态的调用方式应该是类名.静态成员

Person.country ;//调用静态成员
public static void main(String[] args) {
    System.out.println(Person.country);
    Person p1 = new Person();
    p1.name = "张三";
    Person p2 = new Person();
    p2.name = "李四";
    //使用对象p1修改变量country的值
    Person.country = "美国";
    System.out.println(Person.country);
}

静态的调用方式是两种 : 类名调用,对象调用. 非静态成员调用只能对象.

静态成员调用方式只有类名. 非静态成员只能对象. 对象.静态调用方式,会被javac编译为类名调用.

2.4 静态方法

静态方法直接类名调用. 静态方法中不能直接使用非静态成员.

为什么不能调用非静态成员

静态是先人,非静态是后人. 静态出现在内存的时间早于非静态

public class Person {
    String name;
    static String country = "中国";

    /**
     * 静态方法
     */
    public static void eat(){
        System.out.println("人在吃饭" + country);
        System.out.println(name); //错误写法,不允许
    }
}

静态内存优先于对象,在静态的方法中不能使用this和super

2.5 main方法

public static void main(String[] args){
    
}
  • main方法详解

    • public 最大权限 : main方法的调用者是JVM
    • static 无需对象,被类名直接调用,JVM启动的时候使用类名.main启动程序
    • void 无返回值,调用者是JVM,方法的返回值都是返回给调用者,JVM不需要返回值,没有意义
    • main 固定方法名称
    • args 字符串的数组,JVM调用方法main必须传递参数,后期对JVM设置参数

2.6 什么时候定义静态

  • 静态成员变量 : 具体功能具体分析 .

    • 当你需要定义一个类的时候,分析这个类new出来的对象,是否存在共享数据,如果有共享数据,应该定义为静态变量
  • 静态成员方法 :

    • 当你类中的方法,没有使用过非静态成员你的时候,应该定义为静态.

3. 四大权限

四大权限 public protected default private

privatedefaultprotectedpublic
同一类OKOKOKOK
同一包NOOKOKOK
不同包子类NONOOKOK
不同包非子类NONONOOK
  • 受保护权限 protected

    • 权限的成员,为子类提供使用
    • Object类是所有类的父类,类中的方法权限有public,protected
    • 关键的方式受保护权限的方法只能是 子类中super调用!!
    • 子类对象不能调用,不出子类

4. final修饰符

final修饰符是最终的意思,不可改变.final可以修饰类,修饰方法,修饰成员变量,修饰局部变量.

4.1 final修饰类

被final修饰的类,称为最终类,不能被其他的类继承,无子类. 太监类.

学过的final类有哪些, String,System,Scanner.

public final class A{} //这个类A,不能出现子类,如果继承,直接报错
public class B extends A{} //错误,编译错误,最终类不能继承

4.2 final修饰方法

被final修饰的方法,最终方法,不能被子类重写,和调用无关.

一个类中的部分方法很完美,但是另一部分方法有待完成,设计为两个部分.完美的方法就是final

public class A{
    public final void a(){} //方法不能被子类重写
}
public class B extends A{
    public void a(){} //最终方法,不能重写
}

4.3 final修饰局部变量

变量定义在方法的内部,是局部变量, 被final修饰后,一次赋值,终身不改变,锁死了变量的值,看做是常量.

  • final修饰的基本类型,锁死值
  • final修饰的引用类型,锁死内存地址 (引用类型中的成员不受影响)
  • final修饰了方法的参数,调用者传递值后,方法的参数值就锁死
public static void main(String[] args) {
    /**
    *   Student student 对象存储的是内存地址
    *   final修饰后,固定住的,不可改变是student变量保存的地址
    *   但是,Student对象中的成员,不受影响
    */
    final  Student student = new Student();
    student.age = 20;
    student.age = 30;
    System.out.println(student.age);

    final int[] arr = {1,2,3};//arr变量的值,固定为内存地址,不可改变
    arr[1] = 200;

    show(5);
    }

    public static void show(final int x){
    	x = 6; //final修饰,不可改变,报错
    }
}

4.4 final修饰成员变量

成员变量的定义位置,是在类中,方法外面.成员变量在内存中有默认值.final修饰成员变量的时候,锁住的不是内存默认值,而是我们程序人员手动的赋值.

  • 成员变量赋值,可以定义直接写值 int age = 0;

  • 成员变量赋值,可以使用构造方法 public Student(int age){this.age=age;}

  • 成员变量赋值,可以使用set方法完成

    • final修饰的成员变量,可以构造方法赋值,不能set方法赋值
    • 构造方法在new对象的时候,执行一次,仅仅一次
    • 可set方法,反复执行!!
public class Student {
    final int age ;

    public Student (int age ){
        this.age = age;
    }

   /*public void setAge(int age){
       this.age = age;
   }*/
}

5. 代码块

5.1 静态代码块

写在类中方法外面 : static{}

静态代码块的执行时机 : 只要使用了这个类的成员(new对象,调用静态方法,静态变量),静态代码块就会执行,而且就一次

后面的课程会自己写静态代码块 : 数据库连接池 (C3P0,Druid)

JDBC注册数据库驱动程序,使用代码块

5.2 构造代码块

写在类中方法外面的 {}, 创建对象的时候运行,new一次,运行一次

5.3 局部代码块

写在方法内部的 {} 局部代码块,没有用

6. 对象的初始化过程(子类和父类)

  • 父类.class文件先进入内存
  • 子类.class文件再进入内存
  • 初始化父类的静态成员(变量,代码块,方法)
  • 初始化子类的静态成员
  • 运行父类的静态代码块
  • 运行子类的静态代码块
  • 运行父类的构造代码块
  • 运行父类的构造方法
  • 运行子类的构造代码块
  • 运行子类的构造方法

7. 内部类

概述 : 所谓内部类,就是在一个类的内部,定义了另外的一个类

class A{ //外部类,封闭类
    class B{} //内部类,嵌套类
}

对象是生活中的存在的事物,一个事物中还存在着另一个具体的事物

class 大楼{
    class 电梯{}
}

7.1 成员内部类

成员内部类,是一个类定义在了另一个类的成员位置.这个内部类可以使用成员修饰符,public static final private .

对于内部来说 : 可以直接使用外部类的成员,如果外部类要使用内部类的成员,必须要创建对象.

//公式 : 外部类名.内部类名 = new 外部类对象().new 内部类对象()

//外部类
public class Outer {

    public void outer(){
        System.out.println("外部类的方法outer");
    }

    //内部类
    public class Inner{
       public void inner(){
           System.out.println("内部类的方法inner");
       }
    }
}
public static void main(String[] args) {
    //调用内部类的方法inner()
    Outer.Inner oi = new Outer().new Inner();
    oi.inner();
}

问题 : 内部类Inner,编译后有class文件吗?有class文件,名字是外部类$内部类

内部类也是类,继承Object,可以实现接口

内部类是静态的调用方式 : 外部类名.内部类名 变量名 = new 外部类.内部类()

7.2 局部内部类

局部内部类 : 要定义在方法里面. 方法里面是局部位置,不能使用成员修饰符,权限,静态不能用

class A{
    public void a(){
        class B{} //局部内部类
    }
}
public class Outer {
    /**
     *  Inner类,是方法Outer的局部
     *  依然方法,才能被外界访问
     */
    public void outer(){
        class Inner{
            public void inner(){
                System.out.println("局部内部类的方法!!");
            }
        }
        //方法,建立对象
        Inner inner = new Inner();
        inner.inner();
    }
}
public static void main(String[] args) {
    //调用内部类的方法inner()
    //直接调用,不能调用
    Outer outer = new Outer();
    outer.outer();
}

局部内部类,访问局部变量,变量必须final修饰

7.3 匿名内部类

匿名内部类,就是没有名字的内部类,只能写在方法中,为了简化代码书写.

简化 : 实现类,实现接口,重写方法,创建对象. 或者是子类继承父类,重写方法,创建对象.代码上少内容.

  • 匿名内部类使用的前提 :

    • 必须有接口实现,或者是类的继承

    • 格式 :

      new 接口或者父类(){
          //重写抽象方法
      };
      格式 == 实现类,实现接口,重写方法,创建对象
      
    public interface MyInter {
        public abstract void inter();
        public abstract void inter2();
    }
    
    public class InnerClassTest {
        public static void main(String[] args) {
            //匿名内部类,简化书写,不写实现类
            //同时调用多个重写方法
            /*
             *  new MyInter(){}; 是接口实现类的匿名对象
             * 多态 : 接口 变量 = 实现类对象
             */
           MyInter my =  new MyInter(){
    
                @Override
                public void inter() {
                    System.out.println("实现类实现接口重写方法");
                }
    
                @Override
                public void inter2() {
                    System.out.println("实现类实现接口重写方法2222");
                }
            };
           my.inter();
           my.inter2();
        }
    }
    

8. 非法修饰符组合

非法的修饰符的组合,主要说的是抽象abstract

  • abstract和private就是非法组合,抽象方法要重写,private不能继承
  • abstract和final就是非法组合,抽象方法要重写,final修饰不能重写
  • abstract和static就是非法组合,静态方法类名直接调用

九、string详解

3. String字符串类

字符串对象,程序中定义""都是字符串对象,这个对象的使用频率最高.

字符串类 java.lang.String类,继承Object类,实现了三个接口.

程序中只要你写 "里面任意" 都是String类的对象.

字符串对象是常量,一旦创建不能修改.

3.1 字符串对象创建

public static void main(String[] args) {
    //字符串创建,2个方式
    //直接=创建
    String s = "abc";
    //使用构造方法创建
    String str = new String("aa");
}
  • 直接 = 创建方式,代码少,书写简单,推荐使用
  • new String() 使用了构造方法的创建形式,代码大,不推荐使用

3.2 字符串的实现原理

字符串这个数据类,在Java中是不存在的,字符串的实现原理是用char[]数组表示.

"abc",使用数组char[] ch = {'a','b','c'} ;来表示.

JDK9版本之后,节约内存,char数组改变为了byte数组

JDK8版本以前都是char数组

private final char value[]; //JDK中String类的源码

数组的前面的修饰符final, 最终的数组,数组一旦建立,数组的地址就被锁死(常量)使用常量的原因,为了线程安全

3.3 字符串创建对象的区别

String str = "abc"; 
String str = new String("abc");
public class StringTest {
    public static void main(String[] args) {
        String s1 = "abc";
        String s2 = new String("abc");
        System.out.println(s1 == s2); //false

        System.out.println("==========");
        /**
         *  s3 = hello 内存中出现String对象,里面是char数组
         *  s3保存的是String对象
         *
         *  s4 = "hello" 和s3中的字符串在内存中的数组表现是一样的
         *  共用
         *  s3的内存地址,赋值给s4
         */
        String s3 = "hello";
        String s4 = "hello";
        System.out.println(s3 == s4); //true

        System.out.println("========");

        String s5 = "how";
        String s6 = "you";

        String s7 = "howyou";
        /**
         *   s7 == (s5+s6)   s5和s6是变量
         *   变量在编译的时候,javac不确定变量的计算结果是什么
         *   运行的时候,JVM会为 s5+s6的结果,新开内存空间
         */
        System.out.println(s7 == (s5+s6)); //false

        System.out.println("============");

        /**
         *  "how"+"you" 是常量,值在编译期间就已经确定
         *  运行,不会建立新的内存空间
         */
        System.out.println(s7 == ("how"+"you"));// true

        String s8 = "a"+"b"+"c";

    }

    public static void print(){
        //字符串的不变
        //abc内存是不会改变
        String s = "abc";
        System.out.println(s);
        //变量s,指向了新的字符串对象
        s = "bbc";
        System.out.println(s);
    }
}

3.4 String类的构造方法

讲过字符编码, ASCII, 小写字母a的值97

  • String(byte[] b) 字节数组转成字符串,使用平台的默认字符集
  • String(byte[] b,int off,int len) 字节数组转成字符串,使用平台的默认字符集,参数off数组的开始索引,len要转的个数
  • String(byte[] b,int off,int,len,String,charsetName) 字节数组转成字符串,使用平台的默认字符集,参数off数组的开始索引,len要转的个数,charsetName参数是你自己可以指定编码表
public class StringTest {
    public static void main(String[] args) throws UnsupportedEncodingException {
        stringConsByte3();
    }

    //String类构造方法相关,和字节,汉字相关
    public static void stringConsByte3() throws UnsupportedEncodingException {
        // String(byte[] bytes)通过使用平台的默认字符集解码指定的 byte 数组,构造一个新的 String。
        //平台是操作系统, 默认字符集是GBK
        //强制指定为GBK编码
        byte[] bytes ={-28, -67, -96, -27};
        String str = new String(bytes,"gbk");
        System.out.println(str);
    }

    //String类构造方法相关,和字节,汉字相关
    public static void stringConsByte2(){
        // String(byte[] bytes)通过使用平台的默认字符集解码指定的 byte 数组,构造一个新的 String。
        //平台是操作系统, 默认字符集是GBK
        //IDEA 启动的时候,为JVM添加启动参数,默认字符集改成UTF-8

        byte[] bytes ={-28, -67, -96, -27, -91, -67}; // 6字节的数组,转为字符串后是2个汉字
        String str = new String(bytes);
        System.out.println(str);
    }

    //String类构造方法相关,和字节
    public static void stringConsByte(){
       // String(byte[] bytes)通过使用平台的默认字符集解码指定的 byte 数组,构造一个新的 String。
        //平台是操作系统, 默认字符集是GBK
        byte[] bytes = {97,98,99,100};
        String str = new String(bytes);
        System.out.println(str);

        //数组的一部分转成字符串
        String str1 = new String(bytes,1,2);//从1索引开始,要2个
        System.out.println(str1);
    }
}
  • String(char[] b) 字节数组转成字符串

  • String(char[] b,int off,int,len) 字节数组转成字符串,参数off数组的开始索引,len要转的个数

    //String类的构造方法,new String(char[])
    public static void stringConsChar(){
        char[] ch = {'a','b','c','d','e'};
        //构造方法,数组转成字符串
        String s = new String(ch);
        System.out.println(s);
    
        //构造方法,数组转成字符串,转一部分
        String s1 = new String(ch,1,3);//从1索引开始,转3个
        System.out.println(s1);
    }
    

3.5 String类的常用方法

String类的判断类型的方法, 返回都是布尔类型

  • boolean equals(Object obj) 字符串之间的比较,两个字符串相同,返回true
  • boolean equalsIgnoreCase(String str ) 字符串之间的比较,两个字符串相同,返回true,忽略大小写
  • boolean startWith(String str)判断字符串是否以另一个字符串开头,是开头就返回true
  • boolean endsWith(String str)判断字符串是否以另一个字符串结尾,是结尾就返回true
  • boolean contains(String str) 判断字符串中是否包含另一个字符串,完全包含返回true
  • boolean isEmpty()判断字符串的长度是不是0,如果是0返回true
public static void  stringMethod(){
        //boolean equals(Object obj) 字符串之间的比较,两个字符串相同,返回true
        //String类继承Object,重写父类方法,比较的是字符串的实际内容
        String s1 = new String("abc");
        String s2 = new String("abc");
        boolean b = s1.equals(s2);
        System.out.println(b);

        System.out.println("====================");
        //boolean equalsIgnoreCase(String str ) 字符串之间的比较,两个字符串相同,返回true,忽略大小写
        b = "abcdef".equalsIgnoreCase("ABCDEF");
        System.out.println(b);
        System.out.println("=============");
        //boolean startWith(String str)判断字符串是否以另一个字符串开头,是开头就返回true
        b = "HelloWorld.Java".startsWith("Hello");

        System.out.println(b);
        b = "HelloWorld.Java".endsWith(".Java");
        System.out.println("=============");
        System.out.println(b);

        //boolean contains(String str) 判断字符串中是否包含另一个字符串,完全包含返回true
       b = "how are you".contains("are  ");
        System.out.println("=============");
        System.out.println(b);

        //boolean isEmpty()判断字符串的长度是不是0,如果是0返回true
        b = "".isEmpty();
        System.out.println("=============");
        System.out.println(b);
    }

String类的获取方法,返回值不一定

  • int length() 返回字符串长度,字符串中字符的个数
  • char charAt(int index) 返回指定索引上的单个字符
  • int indexOf(String str) 返回指定的字符串,在当前字符串中第一次出现的索引
  • int lastIndexOf(String str) 返回指定的字符串,在当前字符串中最后一次出现的索引
  • String substring(int start,int end)截取字符串,参数表示开始索引和结束索引,包含开头索引,不包含结束索引
  /**
     * String类的获取方法
     */
    public static void stringMethod2(){
        //int length() 返回字符串长度,字符串中字符的个数
        int length = "abcdef".length();
        System.out.println("length = " + length);

        //char charAt(int index) 返回指定索引上的单个字符
        char ch = "abcdef".charAt(3);
        System.out.println("ch = " + ch);

        //int indexOf(String str) 返回指定的字符串,在当前字符串中第一次出现的索引
        //找不到指定的字符串,返回-1  负数不能作为索引出现
        int index = "how do you do".indexOf(" ");
        System.out.println("index = " + index);

        //int lastIndexOf(String str) 返回指定的字符串,在当前字符串中最后一次出现的索引
        index = "how do you do".lastIndexOf(" ");
        System.out.println("index = " + index);

        //String substring(int start,int end)截取字符串,参数表示开始索引和结束索引,
        // 包含开头索引,不包含结束索引
        String str = "HelloWorld";
        str =  str.substring(2,6);// 返回新的字符串
        System.out.println("str = " + str);

        //substring具有重载写法
        String str1 = "你好我好大家好";
        str1 = str1.substring(3);//从3索引开始,截取到最后
        System.out.println("str1 = " + str1);
    }

String类的转换方法

  • String toLowerCase() 字符串中的所有内容转成小写
  • String toUpperCase() 字符串中的所有内容转成大写
  • char[] toCharArray() 字符串转成字符数组
  • byte[] getBytes() 字符串转成字节数组 (查询编码表),平台默认字符集
  • byte[] getBytes(String charsetName) 字符串转成字节数组 (查询编码表),指定编码表
  • static String valueOf(任意类型参数) 参数转成字符串对象
   /**
     * String类的转换方法
     */
    public static void stringMethod3() throws UnsupportedEncodingException {
        //- String toLowerCase() 字符串中的所有内容转成小写
        // - String toUpperCase() 字符串中的所有内容转成大写
        String str = "abCDefGHJktyM";
        String lower = str.toLowerCase();
        String upper = str.toUpperCase();
        System.out.println("lower = " + lower);
        System.out.println("upper = " + upper);

        //char[] toCharArray() 字符串转成字符数组
        char[] ch = str.toCharArray();
        System.out.println(ch);

        //byte[]  getBytes() 字符串转成字节数组 (查询编码表),平台默认字符集
        String s = "呵呵你好";
        byte[] bytes = s.getBytes("gbk");
        for (int i = 0; i < bytes.length; i++) {
            System.out.println(bytes[i]);
        }

       // static String valueOf(任意类型参数) 参数转成字符串对象
        int i = 1;
        String strI = String.valueOf(i);
        System.out.println(strI+1);
    }

String类的比较方法

int compareTo(String str) 字符串之间的比较,谁大谁小,按照字典顺序(自然顺序)

 /**
     * String类的字符串的比较方法,字典顺序
     */
    public static void stringMethod4() throws UnsupportedEncodingException {
        //int compareTo(String str) 字符串之间的比较,谁大谁小,按照字典顺序(自然顺序)
        String str1 = "bcm";
        String str2 = "baz";
        //对象str1调用方法compareTo,参数传递str2
        /**
         *  返回值是int
         *  返回的是 负数,调用者小
         *  返回的是 正数,调用者大
         *  返回是0  一样大
         */
        int i = str1.compareTo(str2);
        System.out.println("i = " + i);
    }

String类的方法 去空格,替换,切割

  • String trim() 去掉字符串两边空格,中间空格不去掉
  • String replace(String oldString,String newString)替换字符串
  • String[] split("规则字符串") 对字符串进行切割
    /**
     * String类的方法 去空格,替换,切割
     */
    public static void stringMethod5() throws UnsupportedEncodingException {
        //String trim() 去掉字符串两边空格,中间空格不去掉
        String str = "  abc  def       ";
        System.out.println(str);
        str = str.trim();
        System.out.println("str = " + str);

        //String[] split("规则字符串") 对字符串进行切割
        String splitStr = "aa,bb,cc,dd,ee"; // 逗号,进行切割
        String[] strs = splitStr.split(",");
        for (int i = 0; i < strs.length; i++) {
            System.out.println(strs[i]);
        }

        //String replace(String oldString,String newString)替换字符串
        String repStr = "how do you do";
        repStr = repStr.replace("o","N");
        System.out.println("repStr = " + repStr);
    }

String类正则表达式相关的功能

正则表达式 : 专门用于处理字符串的技术 (正则大神)

  • 字符类 :

    • [abc] 字符串的这个位置只能是abc
    • [^abc] 字符串的这个位置不能是abc
    • [a-zA-Z] 字符串的这个位置必须是字母,52个
    • [^a-zA-Z] 字符串的这个位置必须不能是字母,52个
  • 数字类:

    • [0-9] 字符串的这个位置只能是数字
    • [^0-9] 字符串的这个位置不能是数字
    • [\d] 等同于 [0-9]
    • [\D] 等同于 [^0-9]
  • 预定义字符 :

    • . 匹配所有的字符
    • [\d] 等同于 [0-9]
    • [\D] 等同于 [^0-9]
    • [\w] 文字字符,包含数字,字母,下划线 [a-zA-Z0-9_]
    • [\W] 文字字符,不能包含数字,字母,下划线 [^a-zA-Z0-9_]
  • 数量词 :

    • X{m} X这个字符只能出现m次 a{3}
    • X{m,} X这个字符至少出现m次
    • X{m,n} X这个字符至少出现m次,不超过n次
    • X? X这个字符出现一次,或者一次也没有
    • X* X这个字符出现零次或者多次
    • X+ X这个字符出现至少一次

正则表达式的匹配功能,String类的方法matches()

/**
     *  检查邮箱
     *  规则 :
     * @ 前面 : 可以是数组,字母,混合,_  位数放下
     * @ 后面 : 数组,字母  sina qq 126 1393 yahoo gmail 位数放下
     * . 固定 : com  cn org  edu gov 字母  位数放下
     */
public static void stringMethod2(){
    String email = "shihehe@sina.com";
    String reg = "[\w]+@[a-z0-9]+(\.[a-z]+)+";
    boolean b = email.matches(reg);
    System.out.println(b);

}
/**
     *  正则表达式检查手机号是否合法
     *  开头必须是1,长度固定11
     *  第二位3 4 5 6 7 8 9
     *  第三位 必须是都是数字
     */
public static void stringMethod(){
    String tel = "13800138000";
    //定义正则的规则,也是字符串
    String regex = "1[3459678][0-9]{9}";
    //正则规则,和字符串校验
    //String类的方法 matches()
    boolean b = tel.matches(regex);
    System.out.println(b);
}

String类的方法split

   public static void stringMethod3(){
        String str = "as123d387654w5465fasfr234567sa";
        String[] strings = str.split("\d+");
        for (int i = 0; i < strings.length; i++) {
            System.out.println(strings[i]);
        }
        System.out.println("================");
        String ip = "192.....168.....35.121";
        String[] ipArray = ip.split("\.+");
        for (int i = 0; i < ipArray.length; i++) {
            System.out.println(ipArray[i]);
        }
    }

String类的方法replaceAll

 public static void stringMethod4(){
        String str = "as123d387654w5465fasfr234567sa";
        //字符串中的所有数组,换成#
        String repString = str.replaceAll("\d+","#");
        System.out.println(repString);

        String first =  str.replaceFirst("\d+","#");
        System.out.println(first);
    }

4. StringBuilder

StringBuilder是字符串对象的缓冲区对象, 缓冲区(出现目的,为了高效)提供String类的效率.

String str = "a"; //字符数组
String str2 = "b";//字符数组
String str3 = str + str2;//字符数组

4.1 StringBuilder类的实现原理

一个可变的字符序列,字符序列就是字符数组

String 类中 : private final char[] value;
StringBuilder : char[] value;

字符序列是数组,Java数组的是定长的,一旦创建,长度固定!

创建对象的时候,StringBuilder中的数组的初始化长度为16个字符

StringBuilder自动的进行数组的扩容,新数组实现,原来数组的中元素复制到新的数组.

结论 : 无论怎么做字符串的操作,StringBuilder内部永远只有一个数组

StringBuilder类是线程不安全的类,运行速度快 , 推荐使用StringBuilder

StringBuffer是线程安全的类,运行速度慢,多线程的程序,使用

两个类的构造方法,和其他的方法,一模一样.