Java SE -- 面向对象

Java 面向对象

类和对象

  • 类: 按照功能来划分
  1. 工具类: 这种类 里面 包含了大量的方法 每个方法里面都有明确的功能
  2. 测试类 :能够运行的类
  3. JavaBean类 :这种类里面 几乎全部都是属性, 每个属性都有private 并且 setget方法。这种类 是用来封装数据用的。
    类是一个模具,而对象是模具做出来的东西。

  • 类:对一类事物的描述 模板 设计图…
  • 成员变量:对应事物的属性
  • 成员方法:对应事物的行为
// 属性 : 成员变量 (类中方法外面)就是该事物的状态信息。

// 行为 :就是该事物能够做什么,行为用方法来表示。

  举例:小猫。

  属性:名字、体重、年龄、颜色。

  行为:走、跑、叫。
  • 对象:是一类事物的具体体现。对象是类的一个实例 ,必然具备该类事物的属性 和行为。
现实中,一类事物的一个实例:一只小猫。

举例:一只小猫。

属性:tom、5kg、2 years、yellow。  

行为:溜墙根走、蹦跶的跑、喵喵叫。
  • 类与对象的关系:
  • 类是对一类事物的描述,是抽象的。
  • 对象是一类事物的实例,是具体的。
  • 类是对象的模板,对象是类的实体。

类是对象的描述
对象是类的实体
根据一个类我可以创建多个对象

类的定义格式

public class ClassName {  
  //成员变量  
  //成员方法  
}
  • 定义类:就是定义类的成员,包括成员变量和成员方法。
  • 成员变量:和以前定义变量几乎是一样的。只不过位置发生了改变。在类中,方法外。
  • 成员方法:和以前定义方法几乎是一样的。只不过把static去掉。

类的组成

  • 属性:在代码中通过成员变量来体现(类中方法外)
  • 行为:在代码中通过成员方法来体现(和前面的方法相比去掉static即可)

对象的使用

  1. 创建对象:类名 对象名 = new 类名();
  2. 使用对象访问类中的成员:
  • 对象名.成员变量;
  • 对象名.成员方法();

内存图

方法区 是用来存储 类的 字节码文件的, 就是代码的存储位置。

堆内存 用来存储 对象的数据的。

栈内存 代码一句一句的执行 在栈里面发生的, 栈里面是以方法为单位。

类的代码存储在 方法区中的。

对象 是创建在 堆内存中的, 我们给对象中的属性赋值, 是赋值到堆内存中的。

  • 类的字节码文件, 只有第一次使用到的时候, 加载一次, 以后再执行到的时候 就不再加载了。
  • 类的字节码文件在 方法区的存储,是跟着jvm的, 只要jvm不停止, 方法区里面的东西就不会清楚。
  • 引用类型的数据 每new一次 就是要在堆内存开辟新的空间。
  • 可以多个变量同时去操作 一个对象(堆内存中的), 只要其中一个通过变量去变量 对象里面的内容,第二变量再去使用对象里面的内容的时候 就已经发生改变了。
  • 当一个应用类型变量 不指向堆内存中任何内容了, 这时候再通过这个变量去访问内容 就会报空指针异常
  • 引用类型作为参数,传递的是地址值。

垃圾回收

  • 栈内存:方法被调用 就会在栈内存存在,
  • 方法执行结束之后,从栈内存中消失了,包括里面的 变量 以及基本类型的数据值 都消失了。
  • 堆内存:当指向这个堆内存中的对象的 所有的变量 都消失了的时候。
  • 这时候 这个对象 就会变成内存垃圾,但是并不会立刻消失。 而是 java底层有一个垃圾回收机制 定时轮训 每隔一段时间就去检测垃圾,并且回收垃圾。
  • 方法区:方法区中的内容, 只要jvm不关闭,任何时候都不消失。

注意:当堆内存中,对象或者数组产生的地址,通过任何方式都不能被找到之后,就会被内存判定为垃圾,垃圾会被java垃圾回收器空闲的时候自动进行清理。

成员变量和局部变量的区别

img

类: 按照功能来划分

  • 工具类: 这种类 里面 包含了大量的方法 每个方法里面都有明确的功能,这种类里面通常没有属性。
class Uitls {
    public void show(){
          
    }

    public void method(){
          
    }
        
    ....
}
  • 测试类 :能够运行的类,含有main方法。
class Demo {
  public static void main(String[] args){
            
  }
}

class Demo1 {
  @Test
  public void show(){
        
  }
}
  • JavaBean类 :这种类里面 几乎全部都是属性, 每个属性都有private 并且 setget方法。这种类 是用来封装数据用的。
public class Student {
          private int age;
          private String name;

          public Student() {
          }

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

          public int getAge() {
            return age;
          }

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

          public String getName() {
            return name;
          }

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

        class Demo {
          public static void main(String[] args){
            //数组 
            //int[] arr = new int[]{10,20,30};

            int[] arr = new int[2];
            arr[0] = 10;
            arr[1] = 20;

            //Student stu = new Student(13,"zhangsan");

            Student stu = new Student();
            stu.setAge(13);
            stu.setName("张三");
          
          }
        }

方法

方法:就是将一个功能抽取出来,把代码单独定义在一个大括号内,形成一个单独的功能。

当我们需要这个功能的时候,就可以去调用。这样即实现了代码的复用性,也解决了代码冗余的现象。


  • 方法作用: 是用来降低重复度的。
  • 循环也是用来降低重复度的,那他俩有什么区别呢?

循环只能提高局部代码的复用性, 但是方法可以提高全局代码的复用性。

方法命名规则

方法名:首字母小写和驼峰原则 例:run(),runRun()

方法的定义

  • 格式一:无参数、无返回值
public static void 方法名(){  
    语句;  
}  
  
public static 写死的。  
void 方法没有返回值。
  • 格式二:有参数、无返回值
public static void 方法名(数据类型 变量名,数据类型 变量名,数据类型 变量名,.... ){  
    方法体;//很多条语句  
}
  • 格式三:有参数、有返回值
public static 数据类型 方法名(数据类型 变量名,数据类型 变量名,数据类型 变量名,.... ){  
    方法体;//很多条语句  

return 返回值;  
}
  • 方法的完整格式:
修饰符 返回值类型 方法名(参数类型 参数名){
    ...
    方法体
    ...
    return 返回值;
}
修饰符 返回值类型 方法名(参数类型 参数名){ 
    ...
    方法体
    ...
    return 返回值; 
}

方法包含一个方法头和一个方法体。下面是一个方法的所有部分:

  • 修饰符:修饰符,这是可选的,告诉编译器如何调用该方法。定义了该方法的访问类型。

  • 返回值类型 :方法可能会返回值。returnValueType 是方法返回值的数据类型。有些方法执行所需 的操作,但没有返回值。在这种情况下,returnValueType 是关键字void。

  • 方法名:是方法的实际名称。方法名和参数表共同构成方法签名。

  • 参数类型:参数像是一个占位符。当方法被调用时,传递值给参数。这个值被称为实参或变量。参
    数列表是指方法的参数类型、顺序和参数的个数。参数是可选的,方法可以不包含任何参数。

    • 形式参数:在方法被调用时用于接收外界输入的数据。
    • 实参:调用方法时实际传给方法的数据。
  • 方法体:方法体包含具体的语句,定义该方法的功能。

方法定义的注意事项:

  1. main方法调用另外一个方法, 等待着另一个方法执行结束之后,main才能继续

方法的注意事项:

  1. 方法不能嵌套
  2. 方法和方法之间是没有先后顺序的。
  3. 方法不调用不会执行的, 因为我们程序的入口都是main方法。
  4. void的方法,说明方法内是不能有返回值的, 但是你是可以写一个 单独的return的。用来强制终止方法。
  • return 这个关键字的作用, 两个作用

    • 第一个:return 强制终止方法的作用,return 后面的语句永远执行不到。
    • 第二个: return 返回值, 把 值扔给调用者
  1. 如果一个方法有返回值类型, 那么这个方法 则必须各个逻辑上,都应该有返回值。
    • 返回值类型,必须要和 return 语句返回的类型相同,否则编译失败 。
    • 不能在 return 后面写代码, return 意味着方法结束,所有后面的代码永远不会执行,属于无效代码。

方法调用

方法必须先定义后调用,否则程序将会报错

方法在定义完毕后,方法不会自己运行,必须被调用才能执行,我们可以在主方法main中来调用我们自己定义好的方法。

  1. 直接调用:直接写方法名调用
public static void main(String[] args) {  
    print();  //调用方法
}
  1. 赋值调用:调用方法,在方法前面定义变量,接收方法返回值
public static void main(String[] args) {  
  int sum = getSum(5,6);     //调用方法
  System.out.println(sum);  
}  
public static int getSum(int a,int b) {  
  return a + b;  
}

3.输出语句调用: 在输出语句中调用方法, System.out.println(方法名()) 。

  • 不能用输出语句调用 void 类型的方法。因为方法执行后没有结果,也就打印不出任何内容。

方法调用过程

  • 方法没有被调用的时候,都在方法区中的字节码文件(.class)中保存
  • 方法被调用的时候,需要进入到栈内存中运行

形参和实参

  • 形参:形式参数,是指方法定义中的参数
  • 实参:实际参数,方法调用中的参数

方法的重载

  • 概念: 同一个类中 出现了方法名相同 参数列表必须不同, 返回值类型无关,这些方法相互重载。
  • 作用: 有了重载之后, 方便程序员的记忆。 减少了负担。

方法重写

  1. 重写 和重载的区别:

重写: 子父类关系中,子类出现了 方法声明 和父类的方法 一模一样的方法 , 就是方法的重写。

重载: 在同一个类中 方法名相同 参数列表不同 返回值类型无关

  1. Override注解
  • 用来检测当前的方法,是否是重写的方法,起到【校验】的作用

方法重写的注意事项

  1. 私有的方法不能重写, (父类私有成员子类是不能继承的)
    是因为 子类访问不了父类的私有方法 ,访问不了 也就重写不到。

并不是 只有父类私有的子类访问不了, 父类其他权限修饰符的 的一些方法 子类也有可能访问不到。

  1. 静态的方法 不能被重写
  2. 子类重写父类方法权限修饰符大于等于父类方法的权限修饰符
  • 静态方法不能被重写,如果子类也有相同的方法,并不是重写的父类的方法

示例代码

public class Fu {
    private void show() {
        System.out.println("Fu中show()方法被调用");
    }

    void method() {
        System.out.println("Fu中method()方法被调用");
    }
}

public class Zi extends Fu {

    /* 编译【出错】,子类不能重写父类私有的方法*/
    @Override
    private void show() {
        System.out.println("Zi中show()方法被调用");
    }
   
    /* 编译【出错】,子类重写父类方法的时候,访问权限需要大于等于父类 */
    @Override
    private void method() {
        System.out.println("Zi中method()方法被调用");
    }

    /* 编译【通过】,子类重写父类方法的时候,访问权限需要大于等于父类 */
    @Override
    public void method() {
        System.out.println("Zi中method()方法被调用");
    }
}

历史遗留问题:

方法的重写:

1:私有的不算重写

public class demo1 {
          public static void main(String[] args) {
            Fu f = new Zi();
            //f.show(); // 报错  说明 不是重写
            //show 是私有的 只能在 class fu 里面去用的。
            f.method();
          }
        }

        class Fu{
          private void show(){
            System.out.println("fu");
          }

          public void method(){

          }
        }
        class Zi extends Fu{
          public void show(){
            System.out.println("zi");
          }
        }

2:静态的也不算重写

public class demo1 {
          public static void main(String[] args) {
            Fu f = new Zi();
            f.show();  //fu
          }
        }

        class Fu{
          public static void show(){
            System.out.println("fu");
          }
        }
        class Zi extends Fu{
          public static void show(){
            System.out.println("zi");
          }
        }

3. 之前我们总说 重写父类的方法 方法声明必须一模一样。 其实返回值可以不一样。

这个不一样,也是有关系的

子类方法的返回值类型 必须 是 父类方法返回值类型的子类。

public class demo1 {
          public static void main(String[] args) {
            Fu f = new Zi();

            Person show = f.show();
            System.out.println(show); //test.day04.demo1.Student@f5f2bb7
          }
        }

        class Fu{
          public Person show(){
            //System.out.println("fu");
            return new Person();


          }
        }
        class Zi extends Fu{
          public Student show(){
            //System.out.println("zi");
            return new Student();
          }
        }
        class Person{}
        class Student extends Person{}

构造方法

  • 定义:用来创建对象 所必需要执行的方法。
  • 定义格式:
  1. 方法名字 必须和类名相同,大小写也要一致。
  2. 构造方法 没有返回值类型的 void也不能有
  3. 构造方法必须 只能用 new来调用。
  4. 不能写返回值,没有具体的返回值 (不能由return带回具体的结果,但是可以单独写一个 return , 目的是为了强制终止构造方法)
class Student {
    public Student(){  // 这就是Student类的一个构造方法。
          
    }
}

构造方法的调用

  • 构造方法的作用是创建对象, 只能使用new 调用, 用来创建对象。不能使用对象去调用。
class Student{
  public Student(){
    System.out.println("构造方法执行");
  }
}

class  Demo {
  public static void main(String[] args){
    Studnet s  = new Student(); // 构造方法执行
    Student s1 = new Student(); // 构造方法执行
    //s.Student(); // 编译报错 
  }
}
      
//-----------------------------------------
class Phone{
  public Phone(){
    System.out.println("Phone构造方法执行");
  }
  public void Phone( ){ // 这个方法 就相当于 一个 普通类似举例那种 show方法。 只不过名字 我不叫show了  叫Phone
  System.out.println("这是一个普通方法 名字叫Phone(恰好方法名和类名相同了 你管得着吗) 不是构造方法");
}
  
  public void show(){
    System.out.println("这是一个普通方法 名字叫show 不是构造方法");
        }
      }
  
  class  Demo {
    public static void main(String[] args){
      Phone s  = new Phone(); // Phone构造方法执行
      Phone s1  = new Phone(); // Phone构造方法执行
      s.Phone();  // 可以 正确  但是 Phone() 这不是构造方法了。
      s.show();
        }
      }

构造方法的执行时机

创建对象的时候调用,每创建一次对象,都会执行一次构造方法。
不能手动的调用构造方法。

构造方法的作用

  • 用于给对象的数据(属性)进行初始化。
    img

构造方法的注意事项

  • 1:构造方法 顾名思义 “构造” 也就是说 这个方法是为了创建对象而存在的,换句话说, 如果一个类中没有构造方法 那么这个类创建不了对象。

构造方法的创建:
如果没有定义构造方法,系统将会给出一个默认的无参构造参数。
如果定义了构造方法,系统将不再提供默认的构造方法。

public class Student {
          /*
          如果一个类中 没有手动写出构造方法
          那么系统 会提供给你一个 构造方法
          public Student(){

          }

          手动写出构造方法之后
          系统就不再提供了
           */

          public Student(){
            System.out.println("sdfsdf");
          }

        }
        public class Demo {
          public static void main(String[] args) {
            Student s = new Student();
            // 然而 这个类却能创建对象
            // 说明这个类中有构造方法。
            System.out.println(s);
          }
        }
  • 2: 构造方法可以重载

如果自定义了带参数的构造方法,还要使用无参数构造方法,就必须再写一个无参数构造方法。
无论之后是否使用,都要手动书写无参构造方法和有参数的构造方法。

public class Phone {
          /*
          构造方法 是不是属于方法。
          方法的重载
            构造方法也必然存在着重载这种现象
            方法名名相同 参数列表必须不同
           */
          /*public Phone(){
            System.out.println("没有参数的构造方法--无参构造");
          }*/
          public Phone(int a){
            System.out.println("有参数的构造方法--有参构造");
          }

          /*
          如果我们没有手动写出构造方法
          系统将会默认提供一个 无参数的构造方法
          public Phone(){ }

          如果我要手动给出了 系统就不再提供了。
           */

        }
        public class Demo1 {
          public static void main(String[] args) {
            //Phone p = new Phone(); //编译报错

            Phone p1 = new Phone(10);

          }
        }
  • 3: 构造方法作用, 在创建对象的时候 顺便给 属性赋值
public class Car {
          private String brand;
          private int price;

          public Car(){

          }

          public Car(String brand, int price){
            this.brand = brand;
            this.price = price;
          }

          public void setBrand(String brand) {
            this.brand = brand;
          }

          public void setPrice(int price) {
            this.price = price;
          }

          public int getPrice() {
            return price;
          }

          public String getBrand() {
            return brand;
          }
        }
        public class Demo2 {
          public static void main(String[] args) {
            // 构造方法是创建对象而生的。
            // 系统给一个 我也能创建对象, 你给了也能用你的 , 有什么区别吗?

            Car c = new Car();
            c.setBrand("宝驴");
            c.setPrice(199);
            //这就是我们创建一个 汽车对象 并把他的属性 弄好的。
            // 三句话。 你用了三条语句。

            // 我觉得三条语句有点麻烦,
            //以后万一 他有1万个属性呢 你需要写1万01条语句

            // 那能不能 一条语句结束呢
            // 无论你想用几条语句 你必须得有创建对象的那条语句。
            // 那我就在想 能不能在创建对象的时候 就给我赋值呢??

            Car c1 = new Car("奔马",299);
            System.out.println(c1.getBrand());
            System.out.println(c1.getPrice());

            // 你有了 有参构造之后 你还要 set方法干嘛啊?
            // 反正 构造方法可以赋值。

            // 你不想要set了
            // 你要充分了解一下 set方法的作用。
            //1:赋值
            // 2: 需求: 刚才那个 奔马 这个名字 我好像起错了 应该是 奔驹

            // 假设此时 没有set方法。

            //c1 = new Car("奔驹",299);  //  你要这么做 就是把之前的那个车 报废掉。 重新造一辆车 九尾了改个名字

            //set方法
            c1.setBrand("奔驹");

          }
        }

构造方法

子类在创建对象的时候,必须去调用父类的构造方法(语法规定),为什么语法这个规定呢?

是因为:子类创建对象要使用的时候,保不准就会用到父类的内容, 所以 一定是先去把父类在内存中初始化,那么如何把父类在内存中初始化么,

就是访问父类的构造方法,这就是在内存中初始化。 父类初始化完了之后,子类再继续初始化子类的。

所以 构造方法的访问特点有如下几个:

1: 子类的所有构造方法的第一行 都默认有一个 super();(只不过你看不到而已) 用于访问父类的无参构造。

2: 而且当你显示给出 super(), super(参数),, 系统就不再默认提供 super()

3: 访问构造方法的语句 必须放在 构造方法的第一行。

4: 当父类没有无参构造方法的时候, 你必须要做 子类初始化之前先初始化 父类这一点,

访问 父类的其他有参构造。

访问 子类其他的构造方法。 无论哪一种, 你只需要做到 子类初始化之前 必须先初始化父类就可以。

5: 子类初始化之前,必须先初始化父类,初始化几次 一次(必须一次),所以 this() 和super() 都必须放在第一行,所以他俩不能共存。

以上细节:你只需要记住原理,就不会忘。 子类初始化之前必须先初始化父类,而且必须是初始化一次。

2.7 方法重写的注意事项

1. 私有的方法不能重写, (父类私有成员子类是不能继承的)

是因为 子类访问不了父类的私有方法 ,访问不了 也就重写不到。

并不是 只有父类私有的子类访问不了, 父类其他权限修饰符的 的一些方法 子类也有可能访问不到。

2. 静态的方法 不能被重写

3. 子类重写父类方法权限修饰符大于等于父类方法的权限修饰符

  • 静态方法不能被重写,如果子类也有相同的方法,并不是重写的父类的方法

示例代码

public class Fu {
    private void show() {
        System.out.println("Fu中show()方法被调用");
    }

    void method() {
        System.out.println("Fu中method()方法被调用");
    }
}

public class Zi extends Fu {

    /* 编译【出错】,子类不能重写父类私有的方法*/
    @Override
    private void show() {
        System.out.println("Zi中show()方法被调用");
    }
   
    /* 编译【出错】,子类重写父类方法的时候,访问权限需要大于等于父类 */
    @Override
    private void method() {
        System.out.println("Zi中method()方法被调用");
    }

    /* 编译【通过】,子类重写父类方法的时候,访问权限需要大于等于父类 */
    @Override
    public void method() {
        System.out.println("Zi中method()方法被调用");
    }
}

构造方法

子类在创建对象的时候,必须去调用父类的构造方法(语法规定),为什么语法这个规定呢?

是因为:子类创建对象要使用的时候,保不准就会用到父类的内容, 所以 一定是先去把父类在内存中初始化,那么如何把父类在内存中初始化么,

就是访问父类的构造方法,这就是在内存中初始化。 父类初始化完了之后,子类再继续初始化子类的。

所以 构造方法的访问特点有如下几个:

1: 子类的所有构造方法的第一行 都默认有一个 super();(只不过你看不到而已) 用于访问父类的无参构造。

2: 而且当你显示给出 super(), super(参数),, 系统就不再默认提供 super()

3: 访问构造方法的语句 必须放在 构造方法的第一行。

4: 当父类没有无参构造方法的时候, 你必须要做 子类初始化之前先初始化 父类这一点,

访问 父类的其他有参构造。

访问 子类其他的构造方法。 无论哪一种, 你只需要做到 子类初始化之前 必须先初始化父类就可以。

5: 子类初始化之前,必须先初始化父类,初始化几次 一次(必须一次),所以 this() 和super() 都必须放在第一行,所以他俩不能共存。

以上细节:你只需要记住原理,就不会忘。 子类初始化之前必须先初始化父类,而且必须是初始化一次。

内存图

方法区 是用来存储 类的 字节码文件的, 就是代码的存储位置。

堆内存 用来存储 对象的数据的。

栈内存 代码一句一句的执行 在栈里面发生的, 栈里面是以方法为单位。

类的代码存储在 方法区中的。

对象 是创建在 堆内存中的, 我们给对象中的属性赋值, 是赋值到堆内存中的。

  • 类的字节码文件, 只有第一次使用到的时候, 加载一次, 以后再执行到的时候 就不再加载了。
  • 类的字节码文件在 方法区的存储,是跟着jvm的, 只要jvm不停止, 方法区里面的东西就不会清楚。
  • 引用类型的数据 每new一次 就是要在堆内存开辟新的空间。
  • 可以多个变量同时去操作 一个对象(堆内存中的), 只要其中一个通过变量去变量 对象里面的内容,第二变量再去使用对象里面的内容的时候 就已经发生改变了。
  • 当一个应用类型变量 不指向堆内存中任何内容了, 这时候再通过这个变量去访问内容 就会报空指针异常
  • 引用类型作为参数,传递的是地址值。
  • 类当中存在的成员变量在堆内存中也有一份, 堆内存都有默认初始化值。
  • 成员方法不会进入堆内存, 但是会有一块专门的空间存放成员方法的地址 ,通过地址找到方法区字节码文件的成员方法

垃圾回收

  • 栈内存:方法被调用 就会在栈内存存在,

方法执行结束之后,从栈内存中消失了,包括里面的 变量 以及基本类型的数据值 都消失了。

  • 堆内存:当指向这个堆内存中的对象的 所有的变量 都消失了的时候。

这时候 这个对象 就会变成内存垃圾,但是并不会立刻消失。 而是 java底层有一个垃圾回收机制 定时轮训 每隔一段时间就去检测垃圾,并且回收垃圾。

  • 方法区:方法区中的内容, 只要jvm不关闭,任何时候都不消失。

关键字:private

  • private只能修饰 成员 不能修饰局部, 被private修饰的成员 不能在类的外部直接访问的。 只能在本类内进行访问.

  • 在java中,为了保证数据的安全性,我们会把数据定义为private等(私有、封装),如果想要调用就会用到set()方法与get方法或者构造函数方法。这里说的是第一种方法,set()与get(),既然说到这个,就得说一下封装的概念。

  • Setter:赋值 Getter:取值,在哪儿用:在构造方法中初始化属性值的用或者对象名.set/get()进行使用!!!

    • 好处:隐藏内部实现细节,保证代码的安全,同时也可以提升数据的安全性
    • 访问修饰符,从访问的范围由小及大依次是:private default(当什么都不写默认就是这个) protected public
    • 访问修饰符的作用:可以修饰类、接口、属性和方法。通常可以通过访问修饰符来改变所修饰对象的可访问范围。

关键字:this

作用:可以调用本类的成员(变量、方法)解决局部变量和成员变量的重名问题。

  • 变量都遵循就近原则,在执行过程中形参和成员变量重名时会优先使用局部变量,局部变量没有时才会去成员变量中找。

  • 所以就出现了this关键字

    • 方法的第一个参数全部隐藏了,第一个参数就是自身对象的地址值。用this关键字来代替。
  • this打破就近原则:

    • 局部变量是无法通过对象直接调用的:
public class Student {
    int age;

  public void show(){
    int aa=10;
    System.out.println(aa);
  }
}

public class Demo {
  public static void main(String[] args) {
    // 此处如何拿到age
    Student stu = new Student();
    System.out.println(stu.age);

    //如何拿到aa
    //System.out.println(stu.aa);  //编译报错 。局部变量拿不了。 因为 局部变量 在栈里面 随着方法调用才存在, 方法没调用 aa是不存在的

    // 所以局部变量 只有在show方法内部去使用。
    stu.show();
  }
}
  • this代表的是对象(指的是当前自己对象的地址)。 对象.变量 此处调用的变量是 成员变量。 所以this.a 此处的a就是成员变量。
class Student {int a = 10;  public void show(){  int a = 20;   System.out.println(this.a); //10     }}
  • this 指的是一个"动态"的对象 , 谁来调用包含this的这个方法,this就指的是谁。只有是这个类的对象可以调用, 所以 “谁”只能是本类对象。

构造方法

  • 定义:用来创建对象 所必需要执行的方法。
  • 定义格式:
    1. 方法名字 必须和类名一摸一样
    2. 构造方法 没有返回值类型的, void也不能有
    3. 构造方法必须 只能用 new来调用。用来创建对象。不能使用对象去调用。
    4. 不能写返回值 (不能由return带回具体的结果,但是可以单独写一个 return , 目的是为了强制终止构造方法)
class Student {
		public Student(){  
		// 这就是Student类的一个构造方法。 
		}
}

构造方法创建对象,每创建一次就会执行一次构造方法。

构造方法的注意事项

  • 1:构造方法 顾名思义 “构造” 也就是说 这个方法是为了创建对象而存在的,换句话说, 如果一个类中没有构造方法 那么这个类创建不了对象。

    • 如果一个类中 没有手动写出构造方法,那么系统 会提供给你一个 构造方法。自己写了之后系统就不再提供。
  • 2: 构造方法可以重载

  • 3: 构造方法有参作用, 在创建对象的时候 顺便给 属性赋值

封装

封装特性:

  1. private 提高安全性
  2. 方法 提高了代码的复用性。
  3. 封装数据 JavaBean类–作用用来封装数据的。

  • 封装:隐藏实现细节,仅对外暴露公共的访问方式。
  • private私有化成员属性,提供公开public的getter/setter

将属性抽取到类当中,这是对数据的一种封装。

将代码抽取到方法中,这是对代码的一种封装。

  • 类: 按照功能来划分

    • 工具类: 这种类 里面 包含了大量的方法 每个方法里面都有明确的功能,这种类里面通常没有属性。
class Uitls {
    public void show(){
          
    }

    public void method(){
          
    }
        
    ....
}
  • 测试类 :能够运行的类,含有main方法。
class Demo {
  public static void main(String[] args){
            
  }
}

class Demo1 {
  @Test
  public void show(){
        
  }
}
  • JavaBean类 :这种类里面 几乎全部都是属性, 每个属性都有private 并且 setget方法。这种类 是用来封装数据用的。
public class Student {
          private int age;
          private String name;

          public Student() {
          }

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

          public int getAge() {
            return age;
          }

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

          public String getName() {
            return name;
          }

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

        class Demo {
          public static void main(String[] args){
            //数组 
            //int[] arr = new int[]{10,20,30};

            int[] arr = new int[2];
            arr[0] = 10;
            arr[1] = 20;

            //Student stu = new Student(13,"zhangsan");

            Student stu = new Student();
            stu.setAge(13);
            stu.setName("张三");
          
          }
        }

1. 继承

1.1 继承的实现

  • 继承的概念:继承是面向对象三大特征之一,可以使得子类具有父类的属性和方法,还可以在子类中重新定义,以及追加属性和方法
  • 实现继承的格式:实现继承的格式通过extends实现
  • 格式:class 子类 extends 父类 { }

注意事项:子类继承父类 不能访问父类 私有的内容, (是因为 自己权限不足)

1.2 Java中继承的特点

  • Java中类只支持单继承,不支持多继承

错误范例:class A extends B, C { }

  • Java中类支持多层继承

多层继承示例代码:

public class Granddad {

    public void drink() {
        System.out.println("爷爷爱喝酒");
    }

}

public class Father extends Granddad {

    public void smoke() {
        System.out.println("爸爸爱抽烟");
    }

}

public class Mother {

    public void dance() {
        System.out.println("妈妈爱跳舞");
    }

}
public class Son extends Father {
  // 此时,Son类中就同时拥有drink方法以及smoke方法
}

2. 继承中的成员访问特点

2.1 继承中变量的访问特点

在子类方法中访问一个变量,采用的是就近原则。

1. 子类局部范围找

2. 子类成员范围找

3. 父类成员范围找

4. 如果都没有就报错(不考虑父亲的父亲…)

示例代码

class Fu {
    int num = 10;
}
class Zi {
    int num = 20;
    public void show(){
        int num = 30;
        System.out.println(num);
    }
}
public class Demo1 {
    public static void main(String[] args) {
        Zi z = new Zi();
        z.show();  // 输出show方法中的局部变量30
    }
}

2.2 继承中构造方法的访问特点(理解)

注意:子类中所有的构造方法默认都会访问父类中无参的构造方法

​ 子类会继承父类中的数据,可能还会使用父类的数据。所以,子类初始化之前,一定要先完成父类数据的初始化,原因在于,每一个子类构造方法的第一条语句默认都是:super()

问题:如果父类中没有无参构造方法,只有带参构造方法,该怎么办呢?

1. 通过使用super关键字去显示的调用父类的带参构造方法
2. 子类通过this去调用本类的其他构造方法,本类其他构造方法再通过super去手动调用父类的带参的构造方法
注意: this(…)super(…) 必须放在构造方法的第一行有效语句,并且二者不能共存

2.3 继承中成员方法的访问特点(掌握)

通过子类对象访问一个方法

  1. 子类成员范围找
  2. 父类成员范围找
  3. 如果都没有就报错(不考虑父亲的父亲…)

系统默认在前面加一个this.

1.多态

1.1多态的概述

  • 什么是多态

​ 同一个对象,在不同时刻表现出来的不同形态

  • 多态的前提
    1. 要有继承或实现关系
    2. 要有方法的重写(非必须)
    3. 要有父类引用指向子类对象

有方法的重写(非必须的),之所以很多的教程中说必须有方法的重写,是因为 我们去应用多态的时候99%以上的情况 全部都是用方法的重写, 如果
你没有方法的重写,那么你使用多态就没有任何意义,所以 很多的教程中 必须有方法的重写。

  • 代码演示
class Animal {
    public void eat(){
        System.out.println("动物吃饭");
    }
}

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

public class Test1Polymorphic {
    /*
        多态的前提:

            1. 要有(继承 \ 实现)关系
            2. 要有方法重写
            3. 要有父类引用, 指向子类对象
     */
    public static void main(String[] args) {
        // 当前事物, 是一只猫
        Cat c = new Cat();
        // 当前事物, 是一只动物
        Animal a = new Cat();
        a.eat();

    }
}

1.2多态中的成员访问特点

总体结论: 多态中除了重写的方法是调用的子类的 其他所有所有的东西都是调用父类的 如果父类没有就报错。

  • 代码演示
成员变量:
      class Person {
        int age = 80;
        int a = 20;
      }
      class Student extends Person {
        int age = 25;
        int b = 30;
      }
      class Demo {
        public static void main(String[] args){
          Person p = new Student();
          System.out.println(p.age); // 80 
          System.out.println(p.a); // 20
          //System.out.println(p.b); // 编译报错
        }
      }
      // 结论:多态中调用成员变量 全部都是调用父类的 父类没有就编译报错。
    
成员方法:
      class Person {
        public void show(){
          System.out.println("Person");
        }
        public void method(){
          System.out.println("method");
        }
      }
      class Student extends Person {
        public void show(){
          System.out.println("Student");
        }
        public void function(){
          System.out.println("function");
        }
      }
      class Demo {
        public static void main(String[] args){
          Person p = new Student();
          p.show(); // Student
          p.method(); // method
          //p.function(); //编译报错, 因为父类没有
        }
      }
      //结论: 多态中调用成员方法 除了重写的方法 是调用子类的 其他的方法都是调用父类的 父类没有就报错。

1.3多态的好处和弊端

  • 好处:提高程序的扩展性。

    • 具体体现:定义方法的时候,使用父类类型作为参数,该方法就可以接收这父类的任意子类对象。
  • 弊端:父类的引用不能调用子类特有的内容的。

1.4多态中的转型

  • 向上转型:父类引用指向子类对象就是向上转型
  • 向下转型:格式:子类型 对象名 = (子类型)父类引用;
  • 代码演示
class Fu {
    public void show(){
        System.out.println("Fu..show...");
    }
}

class Zi extends Fu {
    @Override
    public void show() {
        System.out.println("Zi..show...");
    }

    public void method(){
        System.out.println("我是子类特有的方法, method");
    }
}

public class Test3Polymorpic {
    public static void main(String[] args) {
        // 1. 向上转型 : 父类引用指向子类对象
        Fu f = new Zi();
        f.show();
        // 多态的弊端: 不能调用子类特有的成员
        // f.method();

        // A: 直接创建子类对象
        // B: 向下转型

        // 2. 向下转型 : 从父类类型, 转换回子类类型
        Zi z = (Zi) f;
        z.method();
    }
}

3.5多态中转型存在的风险和解决方案 (应用)

  • 风险

如果被转的引用类型变量,对应的实际类型和目标类型不是同一种类型,那么在转换的时候就会出现ClassCastException(类型转换异常)

  • 解决方案
  • 关键字:instanceof
  • 使用格式

变量名 instanceof 类型

通俗的理解:判断关键字左边的变量,是否是右边的类型,返回boolean类型结果

  • 代码演示
abstract class Animal {
    public abstract void eat();
}

class Dog extends Animal {
    public void eat() {
        System.out.println("狗吃肉");
    }

    public void watchHome(){
        System.out.println("看家");
    }
}

class Cat extends Animal {
    public void eat() {
        System.out.println("猫吃鱼");
    }
}

public class Test4Polymorpic {
    public static void main(String[] args) {
        useAnimal(new Dog());
        useAnimal(new Cat());
    }

    public static void useAnimal(Animal a){  // Animal a = new Dog();
                                             // Animal a = new Cat();
        a.eat();
        //a.watchHome();

//        Dog dog = (Dog) a;
//        dog.watchHome();  // ClassCastException  类型转换异常
      
        // 判断a变量记录的类型, 是否是Dog
        if(a instanceof Dog){
            Dog dog = (Dog) a;
            dog.watchHome();
        }
    }

}


类型转换:
      基本类型:
        自动类型转换
          int a = 10;
          long lo = a; 
        强制类型转换
          long lo = 100L;
          int a = (int)lo;
      
      引用类型():
        自动类型转换
          class Animal {
          
          }
          class Cat extends Animal{
          
          }

          Cat c = new Cat(); //猫 (范围小)
          Animal a = c; // 自动类型转换, 多态  , 向上转换型。
          // Animal a  = new Cat(); // 一样的
        
        强制类型转换
          Animal a = new Cat(); // 猫是一只动物, 但是你虽然看到的是动物  他本质是一只猫
          Cat c = (Cat)a;  // 强制类型转换, 向下转型。

          Animal a1 = new Dog(); // 狗是一只动物 但是本质却是条狗
          //Cat c1 = (Cat)a1; // 运行报错  类型转换错误

           
          // instanceof 前面要放一个 对象,  后面要放一个 类的名字
          //  a instanceof A   
          // a 是A的一个实例吗  如果是 那么这个表达式就是true  如果不是 那么这个表达式就是false
          
          boolean b = a1 instanceof Cat;
          if (b){
            Cat c1 = (Cat)a1;
          }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值