首页 > 技术文章 > Java面向对象基础学习

hss945 2020-10-11 18:38 原文

一、面向对象语言编程

​ Java是一门面向对象的编程语言(OOP),万物皆对象

​ 面向对象初步认识,在大多数编程语言中根据解决问题的思维方式不同分为两种编程语言

​ 1、面向过程编程

​ 2、面向对象编程

面向过程 面向对象
区别 事物比较简单,可以使用线性思维解决,具体每一个实现步骤清晰可见 事物比较复杂使用简单的线性思维无法解决,存在对象与对象之间的引用
共同点 1、都是为了解决实际问题的一种方式 2、当解决复杂问题时,面向对象是从宏观角度把握问题的整体,面向过程是从微观角度实现具体细节,两者之间相辅相成
以每天下楼吃饭为例
面向过程 面向对象
1、下楼找餐厅 1、下楼找餐厅
2、看菜品,并熟悉掌握你吃的每一道菜的来源,制作流程,烹饪手法等具体细节 2、我要开吃了 (不关注具体菜的细节)
3、吃这道菜 3、吃完了

二、Java的面向对象编程

​ Java作为面向对象的语言,关于面向对象语言的核心概念。

1、类和对象

​ 类:一类事物的抽象和模板,在显示世界中类就是任意一类事物,在程序中类就是一个描述这类事物的类文件。

​ 对象:在一类食物中,具体的某一个个体就是对象,在程序中对象就是new出来的,有内存空间。

1.1 类和对象的关系

​ 类是抽象的而对象是具体的,对象是由类创建的示例(new出来的)。

​ 类的组成(人类):

​ 类名:给某一类事物取个名字:People

​ 静态的特征称为属性:姓名、年龄、身高、体重(定义变量的语法)

​ 动态的行为称为方法:吃饭、睡觉、打豆豆(方法的定义依然满足之前所学)

​ 类的实现:

​ 在一个类文件(People)中,定义属性和方法

​ 对象的实现:

​ 通过类名创建这个类的对象

注意:类名不能直接访问它里面的属性和方法,必须由类的对象访问

示例:

package com.j2008.init;

/**
 * ClassName: People
 * Description:
 *  创建一个People类  ,并定义这个类的属性(静态的特征)
 *  和 这个类的方法 (动态的行为)
 * @Author: HuSongSong by softeem
 * @Date: 2020/10/10 14:13
 */
public class People {
    // 定义姓名属性   数据类型 和属性名 = [初始值]
    String name="张三";
    // 定义性别属性
    String sex="男";
    // 定义身高属性
    double height =1.75;
    // 定义体重属性
    double weight = 140;

    /**
     * 定义吃饭的行为(方法)
     */
    public void eat(){
        System.out.println("正在吃饭");
    }

    /**
     * 定义睡觉方法
     */
    public void sleep(){
        System.out.println("正在睡觉");
    }

    /**
     * 定义打豆豆方法
     */
    public void playGame(){
        System.out.println("正在打豆豆");
    }
    // 计算两个数相加
    public int add(int a, int b){
        return a+b;
    }
}
public static void main(String[] args) {
        // 不同通过People直接访问它属性和方法 需要创建类的实例,也就是对象
        // 创建对象的过程称为类的实例化
        // 语法: 类名  对象名   =  new  类名() ;
        People people = new People();
        // 这时候才可以通过对象名 访问这个对象具有的属性 和 方法
        // 对象名.属性名
        // 对象名.方法名([实参])
        System.out.println("这个对象的属性name :"+ people.name);
        System.out.println("这个对象的属性 sex :" + people.sex);
        System.out.println("这个对象的属性 weight :"+ people.weight);
        System.out.println("这个对象的属性height:"+ people.height);
        // 调用对象的访问
        people.eat();
        people.sleep();
        people.playGame();
       	int result =  people.add(2,4);
        System.out.println("这个对象还可以计算 结果:"+result);

    }

案例:定义一个汽车类 , 汽车类包含品牌,颜色,价格的属性 ,速度包含 加速 和 减速的行为和 显示汽车基本信息的行为 ,加速的行为每次这个汽车的速度+20迈, 减速行为 汽车随机减速 20迈以内 ,创建一个汽车对象,初始速度40迈, 连续给汽车加速5次 减速5次 问这个汽车剩下的速度是多少?

package com.j2008.init;
/**
 * ClassName: Car
 * Description:
 * @author husongsong
 */
public class Car {
    // 品牌
    String logo;
    // 颜色
    String color;
    // 价格
    double price;
    // 速度
    double speed;
    /**
     * 加速度
     */
    public void addSpeed(){
        // this 表示当前实例的对象,由于现在没有实例,不知道用什么对象名
        // 只能用this代替
        this.speed+=20;   //  this 也表示 car对象 等价于 car.speed
        System.out.println("汽车正在加速");
    }
    /**
     * 减速度
     */
    public void mulSpeed(){
        this.speed-= Math.random()*20;
        System.out.println("汽车正在随机减速");
    }
    /**
     * 显示汽车信息  当程序运行时 这里的this 代表的就是 car对象
     */
    public void showCar(){
        System.out.println("汽车的品牌:"+this.logo +",汽车的颜色:"+this.color
        +",汽车的价格(万元):"+this.price +" 汽车的速度:"+this.speed);
    }
}
package com.j2008.init;
/**
 * ClassName: TestCar
 * Description:
 *
 * @author husongsong
 */
public class TestCar {
    public static void main(String[] args) {
         //创建一个汽车对象  (只有创建了具体的对象,汽车的属性和行为才能使用)
        Car car = new Car();
        // 给汽车的属性 赋值
        car.logo="宝马";
        car.color="黑色";
        car.price=50;
        car.speed= 40;
        //访问汽车的基本信息
        car.showCar();
        // 加速 减速
        for(int i = 0 ;i< 5;i++){
            car.addSpeed();
            car.mulSpeed();
        }
        // 再次访问汽车的速度
        System.out.println("汽车的最终速度:"+car.speed);
    }
}

​ 在类中定义的属性称为“成员属性”,在类中定义的方法称为“成员方法”

1.2 类和类的关系

1.2.1 继承关系

一个类A可以继承另一个类B,这里类A就是类B的子类,类A可以继承类比的属性和方法,也可以定义自己的属性和方法。
public class Father {

}
public class Son extends Father{
    //继承Father类
}

1.2.2 实现关系

​ 实现指的是一个class类实现interface接口(可以是多个)的功能,实现是类与接口之间最常见的关系。

public interface A {
    //    声明一个接口(注意:接口不是类)
}
public class B implements A{
    //    实现接口A
}

1.2.3 依赖关系

​ A类方法中的参数包含了B。

public class Car {    
    public static void run(){
        System.err.println("汽车在跑");
    }
}
public class Driver {
    public void Driver(Car car) {    //使用传递形参形成依赖
        car.run();
    }
    public void Driver() {
        //使用局部变量形成依赖
        Car car = new Car();
        car.run();
    }
    public void Driver1() {
        Car.run();    //使用静态方法形成依赖
    }
}

1.2.4 关联关系

​ 如果在一个类中引用另一个类,那么这两个类属于关联关系。

​ 例如小明同学养了一条狗,如何通过面向对象的方式定义小明同学与狗的关系。

​ 思路:定义一个People类,其中包含name属性,定义一个Dog类,包含dogName,dogColor属性,将People类与Dog类关联,在People类中创建Dog类的引用

public class People {
    String name;
     // 在People类中 建立 People对象与Dog对象的关系
    Dog dog;
    public People(String name){
        this.name = name;  // 将形参name 赋值给 当前对象的成员属性name
    }
}
public class Dog {
    // 由于以下属性 属于狗的特征,所以必须放在Dog类中,而不能放在People类
    String dogName;
    String dogColor;

    /**
     * 创建Dog对象时 初始化狗的基本属性
     * @param dogName
     * @param dogColor
     */
    public Dog(String dogName ,String dogColor){
        this.dogName = dogName;
        this.dogColor = dogColor;
    }
}

public static void main(String[] args) {
        // 先创建一个小明同学
        People people = new People("小明同学");
        System.out.println("people.dog:"+people.dog);
        // 再创建一个 Dog对象
        Dog dog = new Dog("拉布拉多","灰白");
        System.out.println("dog:"+dog);
        //设置dog和people对象的关系
        people.dog = dog;
        System.out.println("people.dog:" +dog);

    }

1.2.5 聚合关系

​ 聚合关系的两个类处于不同的层次,一个是整体,一个是部分。例如汽车和零件。

1.2.6 组合关系

​ 组合关系了表示2者缺一不可,同时存在同时消失。常常会使用构造方法来达到初始化的目的。

public Driver(Car car){   //  构造方法
    mycar = car;   
}

注意:只给出一段代码判断是关联,聚合,还是组合关系,是无法判断的。

2、面向对象的三大特征

2.1 封装(隐藏)

​ 定义:将类中的成员属性进行隐藏(私有化),并提供公有的访问属性的方法。为了最大程度保护类中属性的隐蔽性(不被其他对象改变)。

​ 示例:

​ 生活中的封装: 例如笔记本 的内部结构统一封装了,一般人使用笔记本时不需要了解笔记本的结构,而是直接开机关机使用。

对代码的封装包括两层意思:

​2.1.1 对类的成员属性的封装

​ 将属性私有化(private),提供对属性的访问给属性添加公用的getter和setter方法。

2.1.2 对代码的封装

​ 为了提高代码的复用性,尽量使用方法加参数传递对代码进行封装,并使该方法公有(public)。

package com.j2008.encapsulation;
/**
 * ClassName: People
 * Description:
 *  封装类的成员时,所有的属性私有化,所有的方法公有
 * @author husongsong
 */
public class People {
    private String pname;
    private int age;
    private String sex;
    // 提供 getter 和 setter
    public String getPname(){
        return pname;
    }
    public void setPname(String pname){
        this.pname=pname;
    }
    public int getAge(){
        return age;
    }
    public void setAge(int age){
        // 对成员属性的隐蔽性 可以防止随意对属性更改
        if(age>100 || age<0){
            System.out.println("赋值的年龄不合法");
            return;
        }
        this.age = age;
    }

    public String getSex(){
        return sex;
    }
    public void setSex(String sex){
        this.sex= sex;
    }
    // 通常为了方便给属性赋值,会提供有参构造
    public People(String pname ,int age,String sex){
        this.pname = pname;
        this.age = age;
        this.sex = sex;
    }
    public People(){
    }
 }

测试类:

public class TestPeople {
    public static void main(String[] args) {
        //创建对象
        People people = new People();
        // 不能直接访问私有的成员,只能通过访问方法
        people.setPname("张三");
        people.setAge(120);
        people.setSex("男");
        System.out.println(people.getAge());
    }
}

注意:对于boolean类型的属性,需要使用isXxx返回属性的值。

2.1.3 封装的优点

​ 1、良好的封装可以减少类的耦合性(类与类的关联)。

​ 2、对类中封装的代码可以自由修改,而不会影响其他类 。

​ 3、最大程度提高类中属性的隐蔽性 和对属性的控制 。

2.2 继承

​ 定义:用于定义类与类的联系的方式,一个类可以继承一个类。

​ 概念:

​ 当多个类中都存在相同的属性和行为时,可以将这些共有的属性和行为定义到一个新的类中,让其他类服用这个新类的属性和行为,这种关系就是继承关系。

​ 当满足 XXX是 一个XXX的时候,也是继承关系,例如 苹果是一种水果,其中水果就是父类,苹果就是子类, 水果有多种水果,而苹果只是水果的一种,所以苹果继承水果。

​ 其中 被继承的类是 父类(超类,基类),继承父类的类就是子类(新类,派生类)。

示例:

package com.j2008.extend;
/**
 * ClassName: Animal
 * Description:
 * 父类
 * @author husongsong
 */
public class Animal {
    String animalName="哮天犬";

    public void info(){
        System.out.println("动物名称:"+animalName);
    }
}

public class Dog extends  Animal{
    public void dogInfo(){
        //子类中可直接访问父类的属性
        System.out.println("狗狗信息:"+animalName);
    }
}

2.2.1 子类继承父类,子类拥有父类的哪些属性和方法?

可访问: 子类拥有父类的共有的属性和方法, 同包下面的属性和方法,不同包下的受保护的也可以访问 。

不可访问: 其中子类不能继承 父类的私有的属性和方法、不同包的默认属性和方法 ,不能继承父类的构造器。

2.2.2 子类继承父类,子类如何访问父类的属性和方法?

属性: 子类通过super 关键字访问父类的属性 ,子类通过this关键字访问自己的属性

方法:子类通过super挂件自方法父类的方法, 子类通过this关键字访问自己的方法

注意: 这里的this和super可以省略,省略后子类通过“就近原则”访问属性和方法(子类中存在就访问子类的,子类中不存在,就访问父类的)。

构造器:子类通过super([参数]) 调用父类的构造器, 子类通过this([参数])调用 自己类的其他构造器,其中 super(参数[]) 必须写在子类构造器的第一行

通过在子类构造器手动调用父类的有参构造器给父类的属性赋值 。

public class Employee {
    String ename="张培进";//员工姓名
    double sal=9000 ; // 员工薪资
    
     public Employee(String ename,double sal){
        this.ename = ename;
        this.sal = sal;
    }
 }

public class Manager extends Employee{
    // 奖金
    private double comm;
     public Manager(String ename ,double sal ,double comm){
        // 如何覆盖父类的无参构造器 ,手动调用父类的有参构造
        super(ename ,sal);  // 只能写在第一句
        this.comm = comm;
    }
  }

注意: 子类构造器中默认调用父类的无参构造器

2.2.3 Java中只能是单继承,一个类只能有一个直接父类的父类,可实现多层次继承

​ 子类 -》 父类 -》 父类的父类

​ pupil -> Student-》 People

​ 创建子类对象时,优先创建父类对象,再创子类对象, 执行顺序 最上层父类的构造器 -》 父类构造器 -》子类构造器。

​ 扩展问题:当一个类中存在static元素时,它们的执行顺序是如何?

​ 顺序: 最上层父类的静态块 - 》 父类的静态块-》 子类的静态块- 》最上层 父类的构造块和构造方法-》父类的构造块和构造方法- 》 子类的构造块和构造方法

public class People {
    static{
        System.out.println("People的静态语句块");
    }
    public People(){
        System.out.println("People类的无参构造器");
    }
    {
        System.out.println("People的构造语句块");
    }
}
public class Student extends  People{
    static{
        System.out.println("Student 的静态语句块");
    }
    public Student(){
        // 默认调用父类的构造器
        System.out.println("Student的无参构造器");
    }
    {
        System.out.println("Student的构造语句块");
    }
}
public class Pupil extends  Student {
    static{
        System.out.println("Pupil的静态语句块");
    }
    public Pupil(){
        // 调用它父类的无参构造器
        System.out.println("Pupil类的无参构造器");
    }
    {
        System.out.println("pupil的构造器语句块");
    }
}

public static void main(String[] args) {
          //创建Pupil对象
        Pupil pupil = new Pupil();
    }

运行结果:

People的静态语句块
Student 的静态语句块
Pupil的静态语句块
People的构造语句块
People类的无参构造器
Student的构造语句块
Student的无参构造器
pupil的构造器语句块
Pupil类的无参构造器

2.2.4 重写

2.2.4.1 定义

​ 子类可以继承父类的方法,但是当父类的方法不能满足子类的需要时,子类可以重写父类的方法。

2.2.4.2 重写的必要条件

1、两个方法名必须相同,且存在不同类中(父子关系的类)。

2、子类重写的父类的方法,其方法的参数和返回值必须完全一样,方法的具体实现可不一样。

3、访问修饰符必须大于或等于父类的修饰符 。

注意: 子类的对象调用父类方法时,如果子类重写了父类的方法,则执行子类的方法,没有重写执行父类的方法。

2.3 多态

​ 在继承关系中,每一个类的对象可能呈现不同的状态。

2.3.1 定义

​ 同一种行为,具有不同的表现形式。

2.3.2 实现多态的前提

  • 基于继承关系或基于实现关系的
  • 子类或实现类必须对方法进行重写(没有重写的方法 不具备多态行为)
  • 父类的引用必须指向子类对象( 子类的引用指向实现类的对象)

2.3.3 多态的对象转型

1、子类对象转父类对象时,称为向上转型是默认转换,自动转型 。

 Cat cat = new Cat();
 // 猫类 可以是一只动物     an4的本质还是 猫对象
 Animal an4 = cat;    
// 子类对象转成父类对象 ,是自动转型
 cat.eat();
 an4.eat();

2、父类的引用转成子类对象,称为向下转型,向下转型需要强转 , 为了避免转换错误,需要先判断数据类型是否匹配。

		// 创建一个动物类, 将动物类转成子类引用
         Animal an5 = new Cat();
        //  an5.catchMouse(); // 动物类型对象不能访问 它子类特有的方法
        if(an5 instanceof Cat) {
            Cat cat2 = (Cat) an5;
            cat2.eat();
            cat2.catchMouse();
        }
        if(an5 instanceof  Dog) {
            Dog dog2 = (Dog)an5;
        }else{
            System.out.println("不能转成dog");
        }

instanceof : 判断该对象是否属于 这个类型 (目的:为了避免ClassCastException的发生)。

  • 为什么需要做类型转换呢?

    ​ 有时候我们需要调用子类特用的方法时必须用子类的引用。所有多态对象下父类应用需要强转。

为了避免ClassCastException的发生,Java提供了 instanceof 关键字,给引用变量做类型的校验,格式如下:

//变量名 instanceof 数据类型如果变量属于该数据类型,返回true。

//如果变量不属于该数据类型,返回false

2.3.4 在多态环境型,方法和属性的调用规则

​ 属性: 当子类和父类中存在相同属性时 ,以 对象的左边引用类型为依据,所谓“看左边” 。

​ 方法: 以当前new出来的对象为依据,如果方法重写了,就调用子类方法,如果方法没有重写 调用父类方法。

​ 静态方法: 无论方法是否有重写, 都已对象左边的引用类型为依据。

3、访问修饰符

​ 定义:用于修饰类,属性,方法的关键字都称为访问修饰符。

3.1 public :公共的

可被同一个项目的所有类方法(项目可见性)。

3.2 protected :受保护

可以被自身的类访问

可以被同包下的其他类访问

对于不同包的,存在父子关系的子类可以访问

3.3 default:默认的

可以被自身类访问

可以被同包下的其他类访问

3.4 private:私有的

只能被自身类访问

访问修饰符 同一个类 同一包不同类(子类或非子类) 不同包的子类 不同包
public
protected ×
默认 × ×
private × × ×

示例:

public class Animal {
    //动物名
     public String name;
     //动物颜色
     protected String color;
     // 动物体重
     double weight;
     // 动物性别
     private String sex;
     // 以上四个修饰符 同一个类里面都可以被访问
     public  void animalInfo(){
         System.out.println("动物信息:"+name+" 颜色:"+color+
                 " 体重:"+weight +" 性别:"+sex);
     }
}

public class Dog extends  Animal{

    public void dogInfo(){
        // 访问父类的 属性  子类继承父类可以继承父类中能被访问的成员属性和方法
        System.out.println("动物名:"+name +" 颜色: "+color + " 体重:"+weight);
    }
}

public class TestAnimal {
    public static void main(String[] args) {
         Animal an = new Animal();
         //给属性赋值
          an.name="狗狗";
          an.color="灰色";
          an.weight=5; // 公斤
         // an.sex  私有
    }
}

4、static关键字

​ static表示“静态” ,它可以修饰 属性,方法,代码块 , 在一个类中除了可以定义成员属性、成员方法和构造器以外,还可以定义静态的部分(静态属性,静态方法,静态代码块)。

static 修饰属性:称为 静态属性或类的属性

static修饰方法:称为静态方法或类的方法

static修饰的语句块: 称为静态代码块

​ static修饰的组件不需要通过对象访问,而是直接通过类名访问,在类一加载时会给static修饰的属性和方法分配内存区,这个内存分布在 静态内存区中。后续所有对象操作的是同一个内存区。

示例:

public class Student {
    //  成员属性
    String name;
    // 静态属性  通常static写在public的后面
    static  int age=20;
    // 静态代码块
    static{
        System.out.println("这是静态代码块,在类一加载时,就会被执行,且只执行一次");
    }
    //成员方法  : 既可以访问 成员属性,也可以访问静态属性
    public void getInfo(){
        System.out.println("姓名:"+name +" 年龄:" +age);
    }
    // 静态方法 : 只能访问静态属性,不能访问成员属性(非静态属性
    //   这是为什么? 由于成员属性的存在需要依赖对象 
    // 静态属性和静态方法在创建对象之前就必须初始化并分配内存
    public static void getStaticInfo(){
       // System.out.println("姓名:"+name);  成员属性不能被访问
        System.out.println("静态属性:"+age);
    }
    public Student(){
        System.out.println("无参构造器");
    }
    // 构造代码块
    {
        System.out.println("构造代码块");
    }
    public static void main(String[] args) {
        System.out.println("访问静态属性:"+Student.age);
        // 访问静态方法
        Student.getStaticInfo();
    }

// 类的组件执行顺序
 // 类编译成.class文件被JVM的类加载器加载-》
 // 从上往下初始化static修饰的组件
 // (静态属性,静态代码块,静态方法,其中静态方法调用才执行,静态属性和静态代码块直接分配内存)-》
 //  --》构造代码块-》执行构造器  -》初始化成员属性,成员方法
        Student stu1 = new Student();
        stu1.name="张三";
 // 静态属性可以通过类名访问,也可以通过对象名访问
        stu1.age=21;
        System.out.println(stu1);
        Student stu2 = new Student();
        stu2.name="李四";
        stu2.age=22;
        System.out.println(stu2);
        System.out.println(stu1.name);
        System.out.println(stu1.age); //   22
        System.out.println(stu2.name); //李四
        System.out.println(stu2.age); // 22
        System.out.println(Student.age);//22
    }

4.1 案例一: 静态的变量 在同一个内存中

public class People {
    double height;
    static int score;
    static{
       score++; // 1
    }
   public void setScore(){
       score++; //81  86
   }
   public static void setScore2(){
        score++;
   }
    public static void main(String[] args) {
         People p1 = new People();
         p1.score=80;//静态属性
         p1.setScore();
         People.score=85;
         p1.height= 1.75;
         People p2 = new People();
         p2.setScore(); //86
         p2.height= 1.80;
        System.out.println(p1.score); // 86
        System.out.println(p1.height); //1.75
        System.out.println(p2.score);//86
        System.out.println(p2.height);// 1.80
    }
}

4.2 案例二: 构造代码块和静态代码块 的执行顺序

package com.j2008.statics;
/**
 * ClassName: UserInfo
 * Description:
 * @author husongsong
 */
public class UserInfo {
    //  关于静态的组件 从上往下执行
    // 静态属性  需要先初始化 ,需要new一个对象
    static UserInfo u = new UserInfo(); // 先执行构造代码块 在执行构造器
    static{
        System.out.println("这是静态代码块,只执行一次");
    }
    public UserInfo(){
        System.out.println("这是无参构造器");
    }
    {
        System.out.println("构造代码块");
    }
    public static void main(String[] args) {
         // 结果
        UserInfo u = new UserInfo();
    }
}

运行结果:

构造代码块
这是无参构造器
这是静态代码块,只执行一次
构造代码块
这是无参构造器

5、抽象类

5.1 定义

​ 在已有类的基础上,由于特殊情况将该类设置为抽象的,这个类就是抽象类。

语法:

public abstract class 类{
      // 类的元素
}

**什么情况下需要定义抽象类? **

1、当这个类不需要创建具体的实例时,可将类定义为抽象的

2、当这个类中存在没有实现的方式时(没有方法体的方法),可以将这个类定义抽象的

5.2 抽象类的特点

1、抽象类 不能实例化(不能new) ,通常抽象被当作父类使用

2、抽象类中 可以有抽象方法( 没有方法体的方法) 也可以有普通方法

3、抽象类被当作父类时,它的子类必须重写父类的抽象方法

6、构造器(Construct)

6.1 定义

​ 在创建对象时被自动调用的特殊方法,也称为构造方法。在一个类中除了包含属性,方法以外,还可以包含构造器(构造方法)。

6.2 语法

访问修饰符 类名([参数列表]){
      
  } 

例如

public	class	Student{
    
     //无参构造器
    public	Student	(){
        System.out.println("这是一个无参构造器");
    }
}

6.3 作用

​ 用于创建对象自动调用,并可以给对象的属性赋初始值。

public	class	Student{
         String	name;//对象的属性
       //有参构造器
       public	Student	(String	name1){
            name=name1;
      }
         //注意一个类中如果存在有参构造器,那么它的无参构造器被覆盖。
  }

创建对象:
   Student	stu	=	new	Student("张三");
     //这里会自动调用有参构造器	并将“张三”赋值给name1,由于自动执行以上构造器,将name1的值赋值给name,这个name就是对象的属性
     System.out.print(stu.name);//张三

package com.j2008.construct;
/**
 * ClassName: TestStudent
 * Description:
 * @author husongsong
 */
public class TestStudent {
    public static void main(String[] args) {
        System.out.println("hello");
        // JVM通过类加载器ClassLoad首先加载 Student ,
        // 先初始化静态资源(省略)
        // 然后执行  new 关键字,开辟一个堆内存空间, 自动执行 构造器
        // 然后 这个对象的属性 ,和 方法 被初始化
        Student stu  = new Student();
        // 给对象赋值
        stu.name="";
        //如果想要在创建对象时,直接初始化属性 ,可以使用有参构造器
        Student stu2 = new Student("张三");
        System.out.println("对象的属性:"+stu2.name);
        Student stu3 = new Student("李四",20);
        System.out.println(stu3.name);
        System.out.println(stu3.age);
    }
}

6.4 构造器的重载

​ 构造器的重载与方法的重载类似(在一个类中存在多个方法名的方法,其参数列表不同)

public class Student {
    // 成员属性
    String name;
    int age;
    // 验证1  一个类是否自带一个无参构造器 ,且创建对象自动调用
    public Student(){
        System.out.println("这是无参构造器");
    }

    // 验证2 有参构造器的定义
    public Student(String name1){
        name = name1;
        System.out.println("这是有参构造器");
    }

    // 构造器的重载  : 它与方法的重载类似(在一个类中存在多个方法名的方法,其参数列表不同)
    public Student(String name,int age){
        // this.name 表示当前对象的成员属性     name : 就近原则找形参name
        this.name=name;
        this.age = age;
    }
}

案例

1、 创建一个 狗类定义狗的名称 狗的颜色 狗的体重构造不同的3只狗,通过构造器完成对狗的属性的初始化。

public class Dog {
    String name;
    String color;
    double weight;
    public Dog(String name,String color,double weight){
        this.name=name;
        this.color=color;
        this.weight=weight;
    }
    public void dog(){
        System.out.println("小狗的信息:名字:" + name + ",颜色:" + color + ",体重:" + weight);
    }
}

测试类:

public class TestDog {
    public static void main(String[] args) {
        Dog dog1 = new Dog("小张", "黄", 45);
        Dog dog2 = new Dog("小祁", "灰", 46);
        Dog dog3 = new Dog("小朱", "红", 49);
        dog1.dog();
        dog2.dog();
        dog3.dog();
    }

2、定义一个圆(Round)类 ,定义圆的半径 通过构造器初始化半径,定义方法计算圆的周长 定义方法计算圆的面积

public class Round {
    double r;
    public Round(double r){
        this.r=r;
    }
    //计算圆的周长的方法
    public double C(double r){
        double C=2*Math.PI*r;
        return C;
    }
    //计算圆的面积的方法
    public double S(double r){
        double S=Math.PI*Math.pow(r,2);
        return S;
    }
}

测试类:

public class TestRound {
    public static void main(String[] args) {
        Round round = new Round(5);
        System.out.println("这个圆的周长为:" + round.C(round.r) + ",面积为:" + round.S(round.r));
    }
}

3、玩一个小游戏(石头,剪刀,布),定义一个裁判类(Judger),属性包括裁判姓名,两个玩家对象,判断输赢的方法(赢的一方 加1分,输的一方减1分 平的不加不减。), 定义一个玩家类(Player),包含玩家姓名,积分,和出拳的行为(1:石头:2:剪刀:3:步,)并返回出拳值。 通过构造裁判对象时给玩家对象赋值

开始游戏: 创建1个裁判,2个玩家,分布玩10次,最后统计 玩家输赢和得分

裁判类:

package com.j2008.game;

/**
 * ClassName: Judger
 * Description:
 * 裁判类
 * @author husongsong
 */
public class Judger {
    // 定义裁判的姓名属性  和 玩家对象(可以是空对象)
    String jname;
    // 玩家1
    Player player1;
    // 玩家2
    Player player2;
    // 在创建裁判时指定玩家对象
    public Judger(String jname,Player player1 ,Player player2){
        this.jname = jname;
        this.player1 =player1;
        this.player2 = player2;
    }
    // 给裁判中对应的两个玩家判分
    public void judge(){
       int num1= player1.play(); // 1 :石头 2:剪刀 3:布
       int num2 = player2.play();
       //如何判断
        if(num1-num2 == -1 || num1-num2 == 2 ){
            // player1赢
            player1.score++;
            player2.score--;
            System.out.println(player1.pname+"打赢了!!!");
        }else  if(num2-num1 == -1 || num2-num1 ==2){
            player2.score++;
            player1.score--;
            //  player2 赢
            System.out.println(player2.pname+"打赢了!!!");
        }else{
            System.out.println("两个玩家打平");
        }

    }
}

玩家类:

package com.j2008.game;
/**
 * ClassName: Player
 * Description:
 * 玩家类
 * @author husongsong
 */
public class Player {
    // 玩家姓名
    String pname;
    //玩家分数
    int score;
    //玩家出拳  1:石头 2:剪刀 3: 布
    public int play(){
        //随机生成
        int n = (int)(Math.random()*100);
        if(n<33){
            return 1;
        }else if(n<66){
            return 2;
        }else{
            return 3;
        }
    }
}

测试类:

package com.j2008.game;
/**
 * ClassName: TestGame
 * Description:
 * @author husongsong
 */
public class TestGame {
    public static void main(String[] args) {
        // 创建两个玩家
        Player player1 = new Player();
        player1.pname="张培进";
        Player player2 = new Player();
        player2.pname="祁键";
        //创建裁判
        Judger judger = new Judger("朱正康",player1,player2);
        for(int i = 0;i<10;i++){
            System.out.println("第"+i+"次玩");
            judger.judge();
        }
        //统计结果
        System.out.println(player1.pname +"的分数:"+player1.score);
        System.out.println(player2.pname +"的分数:"+player2.score);

    }
}

7、接口

7.1 定义

​ 接口用于对某件事物的功能的声明,而没有实现具体功能 ,接口提供对软件开发的标准规范。

​ 利用接口的“多实现”完成Java的单一继承。

7.2 接口的好处

1、为了规范实现类的行为,让所有的方法都通过接口定义

2、为了提高代码的可扩展性

7.3 接口中的成员组成部分 特点

​ 接口中定义的变量默认全部是 public static final 修饰的。

​ 接口中的静态方法可以直接调用。

​ 接口中不能写构造器(因为接口不能实例化,不需要构造器)。

​ 接口中的方法全部都是 抽象方法, 在JDK8以后可以定义default的非抽象方法。

案例

1、一个类既可以继承一个类 也可以实现多个接口

//定义一个门类 ,定义一个防盗门,它具有防盗的功能,如何防盗 (防盗功能中可以 拍照, 密码锁)
// 门本 身有 开门,关门 功能public abstract class Door {
    //定义开门  关门的方法
    public void openDoor(){
        System.out.println("门开了");
    }
    public void closeDoor(){
        System.out.println("门是关的");
    }
}

/**
 * 防盗接口
 */
public interface Burglars {
    //定义初始密码
    public static final String pwd ="888888";
    public void takePhoto();
    public boolean enterPassword(String password);
}

public class BurglarsDoor extends  Door implements Burglars {
    @Override
    public void takePhoto() {
        System.out.println("防盗门正在拍照。。。");
    }
    @Override
    public boolean enterPassword(String password) {
        // equals比较两个字符串的值是否相等
        if(password.equals(Burglars.pwd)){
            return true;
        }
        return false;
    }
}

public class Test {
    public static void main(String[] args) {
        // 创建门对象
        //1、第一个门只能开关
        Door door = new BurglarsDoor();
        door.openDoor();
        door.closeDoor();
        // 2 、 防盗功能
        Burglars door2 = new BurglarsDoor();
        door2.takePhoto();
        if(door2.enterPassword("888888")){
            System.out.println("锁打开了,但是门没有开");
        }
        // 3、使用防盗门类创建自己的对象
        BurglarsDoor door3 = new BurglarsDoor();
        door3.closeDoor();
        door3.takePhoto();
        System.out.println("输入密码验证");
        if(door3.enterPassword("888888")){
            door3.openDoor();
        }
    }
}

2、 一个类可以实现多个接口,中间用逗号分隔 (那么这个类要实现接口的所有方法

/**
 *  程序员身份
 */
public interface Programer {
    public void takeMoney();
}
/**
 * 厨师
 */
public interface Cooker {
    /**
     * 做好吃
     */
    public void makeFood();
}
public interface Driver {
    // 功能
    public void driverCar();

    public void takeMoney();
}

public class People implements Programer ,Cooker ,Driver {

    private String name;
    public People (String name){
        this.name = name;
    }
    @Override
    public void makeFood() {
        System.out.println("做好吃的");
    }

    @Override
    public void driverCar() {
        System.out.println("开车的功能");
    }

    @Override
    public void takeMoney() {
        System.out.println("赚钱的功能");
    }
}

public static void main(String[] args) {
         People people = new People("张培进");
         people.takeMoney();
         people.makeFood();
         people.driverCar();
    }

3、一个接口可以继承 接口,或多个接口(接口可以多继承)一个员工接口 一个程序员 (由于程序员是员工,所有程序员可以继承员工接口 )一个人 是员工也是 程序员 (People 是员工 程序员 )

/**
 * ClassName: Employee
 * Description:
 *  员工身份
 * @author husongsong
 */
public interface Employee {
    // 是否有女朋友
    public boolean isGirlFriend();
}

public interface Boss {
    // 老板可以赚更多钱
    public void takeMoreMoney();
}

/**
 * ClassName: Employee
 * Description:
 *  程序员身份
 * @author husongsong
 */
public interface Programer extends Employee ,Boss{
     public void writeCoding();
}

public class Student implements Programer {
    private String name;
    public Student(String name){
        this.name = name;
    }
    @Override
    public void writeCoding() {
        System.out.println(this.name+"写代码");
    }
    @Override
    public boolean isGirlFriend() {
        System.out.println(this.name+"有女朋友");
        return true;
    }
    @Override
    public void takeMoreMoney() {
        System.out.println(this.name+"以后可以赚更多的钱,有更多的女朋友");
    }
}

public class Test {
    public static void main(String[] args) {
        Student stu = new Student("张培进");
        stu.isGirlFriend();
        stu.writeCoding();
        stu.takeMoreMoney();
    }
}

8、总结常见面试题

8.1 说一下方法的重写与重载的区别?

1、重写存在于父子关系中的不同类,重载存在同类中。

2、重写必须满足 方法名相同,且参数相同,返回值相同 ,重载满足方法名相同,参数不同,与返回值无关。

3、重写的访问修饰符必须 子类大于等于父类, 重载没有要求。

8.2 抽象方法和非抽象方法的区别?

​ 抽象方法没有方法体,需要使用abstract修饰, 只能写在抽象类中。

​ 非抽象方法就是普通方法(成员方法或静态方法) ,可以写在抽象类中或普通类中。

8.3 抽象类与接口的区别?

共同点: 它们都是抽象的,都不能实例化

区别:

​ 1、抽象类是类 使用abstract修饰,接口使用interface定义。

​ 2、抽象类中可以包含抽象方法和普通方法, 接口中是纯粹的抽象类,都是抽象方法,除使用default定义以外

​ 3、子类继承抽象类,必须实现抽象类的方法,或者自身变为抽象类

​ 接口中是实现关系,实现类必须实现接口的所有方法,接口的变量是public static final

8.4 在java中如何实现多态(多态的前提条件)?

  • 基于继承关系或基于实现关系的
  • 子类或实现类必须对方法进行重写(没有重写的方法 不具有多态行为)
  • 父类的引用指向子类对象( 接口的引用指向实现类的对象)

8.5 final的作用是什么?

​ final修饰变量 定义为常量

​ final 修饰方法 不能被重写

​ final 修饰类 则不能被其他子类继承

8.6 接口定义实现类的对象 和 实现类定义实现类的对象的区别?

​ 接口定义实现类的对象只能调用接口中定义的方法, 实现类定义的对象既可以调用实现类的方法,也可以调用接口类的方法。

推荐阅读