Java笔记day11

1、封装

在类中定义属性的时候,一般需要把属性隐藏起来。

如果外界需要访问这个属性,那么就提供公共方法对其访问

封装就是将一些属性,特征等封装进一个类中,可以通过调用公共的方法来使用这些属性,特征等。

我们在定义一个类的时候,就是在完成封装的过程,将属性,方法定义在类中

封装的特点

  • 提高的代码的安全性,重要的信息可以私有化,不会让其他类直接访问这些信息,不对外暴露这些信息
  • 提高代码的复用性,常用的代码或者功能封装到方法中,可以在其他地方通过访问该方法来获取这些代码或功能
  • 封装代码的实现细节,以便修改内部带啊吗,提高维护性
  • 简化外部的调用,便于调用者使用

2、方法重载

类中共有多个方法,具有相同的方法名,但是方法的参数各不相同,这种情况被称为方法的重载

public void test(){
    
}
public void test(int a){
    
}

方法的重载要求

  • 方法的重载必须是在同一类中
  • 方法的名字必须相同
  • 方法的参数列表必须不同(参数列表---->参数类型,参数类型的排序,参数个数)
  • 方法的修饰符,返回类型,抛出异常这些地方没有限制(可以相同,也可以不相同,一般习惯性都是相同的)

例如,

public class Test{
    
    //参数的个数不同
    public void test(){}
    public void test(int a){}
    public void test(String s1,String s2){}
    
    //参数的类型不同
    public void test(boolean flag){}
    public void test(double salary){}
    
    //参数的顺序不同
    public void test(int a,String s){}
    public void test(String s,int a){}
    
    //方法的参数可以相同,也可以不同
    public int test(int a,int b){
        return 0;
    }
    
}

特殊情况:

public class Test{
    
    //方法重载
    public void test(int a,long b){}
    public void test(long b,int a){}
    
    public static void main(String[] args){
        Test t = new Test();
        t.test(1,1L);//调用到第一个方法
        t.test(1L,1);//调用到第二个方法
        t.test(1,1);
    }
    
}

注意,这时候,t.test(1,1)代码是会编译报错的,因为类中定义的俩个方法都匹配

注意,当参数无法完全精确匹配到方法的时候,参数会尝试做自动转换,然后再去尝试匹配方法,在t.test(1,1)中,当后面的1去尝试转化为long类型的时候,前面的1也会跟着转换,这就会导致无法匹配与参数相对应的方法。

例如,

public class Test {

    //重载方法1
    public void test(int a){}
    //重载方法2
    public void test(short a){}

    public static void main(String[] args){
        Test t = new Test();
        byte a = 1;
        t.test(a);//这里会调用第二个方法,也就是short类型参数的test方法
    }

}

虽然byte类型数据,可以自动转换为short,也可以转换为int,但是short离byte“更近”,就近原则

3、创建和初始化对象

new Test();这句代码完成了两过程,对象的创建和初始化

  • new关键字,给对象申请/分配内存空间,并将对象中的属性进行默认值的初始化,根据属性类型不同,其默认值分别为:整型(0) 、浮点型(0.0)、char(’\u0000’) 、引用数据类型(null)、boolean(false)

  • 如果在代码中,还给这些代码进行了显示赋值,那么就会用这个显示赋值,把之前的默认值给覆盖掉

    private String name = "凉拌小番茄";
    //原本在创建对象时候,会给name赋一个默认值null,然后我们手动给那么显示赋值,那么此时name的值就变成了 “凉拌小番茄”
    
  • 调用类中的构造器,在构造器中也可以对属性进行赋值,这个赋值会把之前默认值或者显示赋值给覆盖掉(构造器是在创建对象中最后实现的)

注意

这时候对象已经创建出来了,并且属性也有了值(可能是默认值,也可以是显示赋值,也可能构造器中赋的值),如果我们想再设置其他的属性值,可以使用对象访问属性,或者调用方法。

如果是public属性,那么就可以直接使用对象进行访问并赋值。(不推荐,安全性不高)

如果是private属性,那么就需要使用对应的setXxx方法进行属性值的的设置。(推荐

private String name;

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

这里介绍private修饰符的特点

  • private是四种修饰符( public,protected,default,private)中的一种,并且是权限最小的一种
  • private 可以修饰成员变量和成员方法
  • private修饰成的属性和方法,就只在当前类中才能访问,当前类意外的其他地方不可以直接访问
  • private修饰的属性称为私有属性,private修饰的方法称为类中的私有方法

局部变量和成员变量有什么区别

  1. 出现的位置不同
    • 成员变量直接出现在类中
    • 局部变量定义在方法中或者代码块中
  2. 初始化
    • 成员变量会默认初始化(默认值)
    • 局部变量必须要手动进行赋值
  3. 生命周期
    • 成员变量随着对象的创建而初始化,随着对象的回收而消失,成员变量的生命周期跟对象是一致的
    • 局部变量随着方法的栈帧入栈而初始化,随着方法的栈帧而消失,生命周期与方法生命周期一致

4、构造器

类中的构造器也称为构造方法,构造函数,是在创建对象的时候必须要调用的方法

因为创建对象会必须要调用构造方法,所以有一个隐藏的,Java提供的默认构造方法

public class Test{
new Test;
public Test(){
    //这就是Java默认提供的构造方法,这个构造器中不执行任何代码。称为无参构造器
}
}

构造器的特点:

  • 必须与类名保持一致
  • 必须没有返回类型,也不能写void

构造器的作用:

  • 使用new关键字来创建对象的时候,后面跟着必须是类中存在的构造器
  • 构造器中的代码,在对象创建之后会被调用,从而可以完成对象的初始化工作
public class Student{
    private String name;
    private int age;
    //构造器
    public Student(String name,int age){
        this.name = name;
        this.age = age;
    }
    
    public static void main(String[] args){
        //创建对象的时候使用构造器
        //并且传参到构造器中,构造器中可以使用这些参数给属性进行初始化
        Student stu = new Student("tom",20);
    }
    

构造器的重载:

除了默认的无参构造器之外,在类中还可以对构造器进行重载,让构造器可以接收一些参数,然后使用这些参数进行对象的初始化工作。

public class Student{
    private String name;
    private int age;
    
    //无参构造器
    public Student(){}
    
    //有参数构造器,接收参数,进行对象属性的初始化
    public Student(String name){
        this.name = name;
    }
    
    public Student(int age){
        this.age = age;
    }
    
    public Student(String name,int age){
        this.name = name;
        this.age = age;
    }   
    
}

注意构造方法重载之后,或者自己在类中定义了一个构造器时,系统就不会在提供一个默认无参构造器,我们要在类中自己手写一个无参构造器

构造器之间的调用:

使用this关键字,可以在构造器中,调用另一个构造器

public class Student{
    private String name;
    private int age;
    
    //无参构造器
    public Student(){
        //调用俩参的构造器
        this("tom",20);
    }
    
    public Student(String name){
        //调用俩参的构造器
        this(name,0)
    }
    
    public Student(int age){
        //调用俩参的构造器
        this(null,age);
    }
    
    public Student(String name,int age){
        this.name = name;
        this.age = age;
    }   
    
}

注意在一个构造器中无法同时使用两个this调用其他构造器,这是因为this默认调用的就是这个对象,必须要放在方法的第一行,所以只能存在一个,但类似于this.name,this.age可以同时使用是因为,调用的是属性,而不是方法。

5、this

this代表,所在类的当前对象的引用(地址值),即对象自己的引用。

public class Student{
    
    public void sayHello(){
        this.show();//这个this就表示当前类的对象stu
    }
    
    public void show(){
        
    }
    
}

public static void main(String[] args){
    
    Student stu = new Student();
    stu.sayHello();
    
}

在Student类中定义了两个方法,要想调用这俩个方法,就需要创建Student的对象,然后通过这个对象来调用这两个方法

注意类中的非静态方法,需要通过类的对象来调用,没有其他方式,而静态方法可以直接调用(static修饰的方法)

在这一种情况之下,我们要在sayHello方法中调用show方法就需要通过this.show()方法

这里的this代表的就是调用当前类的对象地址,通过地址来操作对象

注意,当我们创建两个对象的时候

每一个对象中,都有自己的this,和其他对象中的互不影响。

当前执行stu1.sayHello()代码的时候,this代表的就是stu1

当前执行stu2.sayHello()代码的时候,this代表的就是stu2

方法被哪个对象调用,方法中的this就代表那个对象。即谁在调用,this就代表谁。

this.name访问的是当前类中name

一个对象的创建

  • new 分配内存(堆的新生代的edan)并且将成员变量默认初始化
  • 如果属性有显示初始化(手动赋值),于是于是就会将数据的默认值覆盖掉
  • 调用合适的构造方法,构造方法完成就意味着对象创建完毕
  • 将对象的地址赋值给相应的变量

6、继承

继承描述的是事物的所属关系,子类继承父类,那么子类就可以继承父类中定义的非私有属性和方法

父类更为通用,子类更为具体(这就好比多个儿子拥有同一个父亲,父亲通用为所有儿子,但每一个儿子都会继承父亲的一些特征,习惯,但每一个儿子各不相同,并且有了属于自己的特征,习惯)

  • 多个类中存在相同属性和行为时,将这些内容抽取到单独一个类中,那么这些类(子类)就不需要再定义这些属性和行为,只要继承同一个类(父类),它们就可以直接访问父类中的非私有的属性和方法。

  • 继承的好处

    1. 提高代码的复用性
    2. 类与类之间产生了关系,这是使用多态特性的前提
  • 继承的关键字----extends

    //定义Person类,作为父类
    public class Person{
        String name;
        public void sayHello(){
            System.out.println("hello~ I am "+name);
        }
    }
    
    //定义Student类,作为子类
    class Student extends Person{
        
    }
    
    public static void main(String[] args){
        Student stu = new Student();
        stu.name = "tom";//name属性从父类中继承而来
        stu.sayHello();//sayHello方法从父类中继承而来
    }
    
  • 子类继承父类,继承了父类中的属性和方法,并可以在子类中访问这些属性和方法:

    //定义Person类,作为父类
    public class Person{
        String name;
        private int age;
        public void sayHello(){
            System.out.println("hello~ I am "+name);
        }
    }
    
    //定义Student类,作为子类
    class Student extends Person{
        //这个是子类中单独定义的方法,和父类无关
        public void hi(){
            name = "jack";//访问从父类中继承过来的属性
            sayHello();//调用从父类中继承过来的方法
            age = 20;//编译报错,age是父类中的私有属性,子类中不能访问
        }
    }
    

    父类中的构造器,子类是不能继承的

java中,类和类之间的继承是单继承,一个类只能有且只有一个父类,不能同时直接继承俩个父类,但是可以间接继承其他类(子类继承父类,父类继承自己的父类,子类也会有自己父类的父类的方法)

java中,如果没有给类指定父类的话,那么这个类会默认继承父类Object

Object类是Java提供的类,每一个类都是直接或者间接继承了Object类。

子类继承父类,创建子类对象的时候,会先默认调用父类的构造器:

//定义Person类,作为父类
public class Person{
    public Person(){
        System.out.println("Person类中的构造器被调用");
    }
}

//定义Student类,作为子类
class Student extends Person{
    
    public Student(){
        System.out.println("Student类中的构造器被调用");
    }
}

public static void main(String[] args){
    new Student();
}

//main方法执行会输出以下语句:
Person类中的构造器被调用
Student类中的构造器被调用

子类继承父类,会继承父类的属性和方法,那么就需要先调用父类的构造器对父类中的属性进行初始化,初始化完成后再给子类使用。

构造器的作用之一就是进行初始化

7、super关键字

在类中,除了可以使用this关键字之外,还可以使用super关键字

在子类中,使用super关键字一般做以下事情:

  • 访问父类中的属性

  • 调用父类中的方法

  • 调用父类中的构造器

访问父类中的属性:

public class Person{
    String name = "zs";
}

public class Student extends Person{
    //注意,这里是故意和父类中的属性重名
    String name = "lisi";
    public void test(){
        //直接使用name,表示Student中的name属性(就近原则)
        System.out.println(name);
        
        //也可以使用this和super来区分访问的是哪个name
        System.out.println(this.name);//Student中的name
        System.out.println(super.name);//父类Person中的name
    }
}

调用父类中的方法:

public class Person{
    public void run(){
        System.out.println("person run..");
    }
}

public class Student extends Person{
	//注意,这里是故意和父类中的方法重名
    public void run(){
        System.out.println("student run..");
    }

    
    public void test(){
        //直接使用name,表示Student中的run方法(就近原则)
        run();
        
        //也可以使用this和super来区分调用的是哪个run方法
        this.run();
        super.run();
    }
}

调用父类中的构造器:

子类构造器中隐式调用父类无参构造器,例如

public class Person{
	public Person(){

    }			
}

public class Student extends Person{
   
    
    public Student(){
		//这里会隐式调用(自动调用)父类的无参构造器
        
    }
}

子类构造器中显式调用父类无参构造器,例如

public class Person{
	public Person(){

    }			
}

public class Student extends Person{
   
    
    public Student(){
		//也可以使用super关键字,显示调用父类的构造器(有参的无参的都可以调用)
        super();
    }
}

子类构造器中显式调用父类有参构造器,例如

public class Person{
	public Person(){

    }	
    public Person(String name){

    }
}

public class Student extends Person{
   
    
    public Student(){
		//也可以使用super关键字,显示调用父类的构造器(有参的无参的都可以调用)
        super("tom");
    }
}

在构造器中,可以使用this调用类中其他构造器,也可以使用super调用父类中的构造器。

但是this和super这俩中调用构造器的代码,不能同时出现,否则会报错。

这是因为this调用构造器的语句和super调用构造器的语句,都要求自己是第一句代码,但是构造器中的第一句代码只能有一个,所以它们俩个不能同时出现,例如

public class Person{
	public Person(){

    }	
   
}

public class Student extends Person{
   
    
    //编译报错,在使用this和super调用构造器功能的时候,它们俩个不能同时出现
    public Student(){
        this("tom");
        super();
    }
    public Student(String name){

    }
}

但是,如果this和super不是都要调用构造器,那么同时出现就没有问题,例如

public class Person{
	public void sayHello(){
        
    }	
}

public class Student extends Person{
   
    
    //编译通过
    public Student(){
        this("tom");
        super.sayHello();
    }
    public Student(String name){

    }
}

思考,在子类的构造器中,为什么要调用父类的构造器?

**如果你生成子类对象时没有调用父类的构造器,那么,我们在使用父类的一些成员变量的时候,就会报变量未初始化的错误。**请记住,变量初始化总是在构造器调用之前完成!

构造造一个对象,先调用其构造方法,来初始化其成员函数和成员变量。子类拥有父的成员变量和成员方法,如果不调用,则从父类继承而来的成员变量和成员方法得不到正确的初始化。

8、方法重写

如果子类和父类中出现了相同的方法,这种情况就叫做方法重写 (Override)。

注意,方法重载是发生在同一个类中,方法重写发生在子父类之间

父类中的一个方法和子类中的一个方法,满足以下要求,就是方法的重写:

  • 方法名必须相同

  • 参数列表必须相同

  • 访问控制修饰符可以被扩大,但是不能被缩小

    public > protected > default > private

  • 方法抛出异常类型的范围可以被缩小,但是不能被扩大

    例如:ClassNotFoundException --扩大–> Exception

    例如:Exception --缩小–> ClassNotFoundException

  • 返回类型可以相同,也可以不同

    • 如果父类的返回类型是引用类型,子类重写后的方法返回类型可以和父类方法的返回类型保持一致,也可以是父类方法返回类型的子类型

      例如,父类方法的返回类型是Person,子类重写后的返回类可以是Person也可以是Person的子类型

    • 如果父类的返回类型是基本类型,那么子类重写后的返回类型必须和父类的保持一致

      例如: 父类方法的返回类型是int,子类重写后的返回类也必须是int

注意,大多数情况下,子类中重写的方法 会和 父类中的方法 完全保持一致,只有方法的实现不同。(也就是大括号中代码不一样)

方法重写和重载的区别的是什么?

overload

  • 发生在本类中
  • 方法名相同 只跟参数列表有关 跟其他的无关

overwirte

  • 发生子父类之间
  • 方法名与父类方法相同,参数列表与父类方法名相同
  • 重写的方法修饰符要么与父类方法相同或者比父类方法大
  • 抛出的异常要么与父类相同要么比父类的方法小
  • 重写方法返回值基本数据类型保持一致
  • 如果是引用类型 要么保持一致要么就是该类型的子类

父类中哪些方法不能被重写:

  • 父类中的静态方法不能被子类重写
  • 父类中的私有方法不能被子类重写

也就是说,只有在子类中可以直接访问到的父类方法,并且是非静态的方法,才能被子类重写

常见的重写情况,例如

public class Person{
	public void sayHello(){
        System.out.println("你好!很高兴认识你");
    }	
}

public class Student extends Person{
	
    //子类中,重写父类的sayHello方法
    public void sayHello(){
        System.out.println("hello!nice to meet you");
    }
}

public static void main(String[] args) {
	Student stu = new Student();
    //由于子类重写了sayHello方法,所以这里将会调用到子类中重写后的sayHello
    //如果子类中没有重写sayHello方法,那么这里将会调用到从父类继承过来的sayHello方法
    stu.sayHello();
}

子类继承父类,在调用方法的时候,如果子类中没用重写,那么调用的时候从父类继承的方法,如果子类重写了这个方法,那么将会调用到子类中重写后的方法。(非常重要

在JavaAPI中,String类继承了Object,并且重写了Object类中的toString、equals等方法:

注意,Object所所有类的父类,每一个类都是直接或间接继承了Object类

所以,在调用String对象的toString、equals等方法时候,其实调用的是子类String中重写后的方法!

思考,一般什么情况下,我们会在子类中,对从父类继承的方法进行重写?

子类继承父类,继承了父类中的方法,但是父类中的方法并不一定能满足子类中的功能需要,所以子类中需要把方法进行重写。

toString方法

toString方法如果重写,打印对象就是打印toString方法里面的内容

联系,下面结果为多少

class A {
	public void say() {
		System.out.println("A");
	}
	public A() {
		this.say();
	}
}


public class AStudent extends A {
	public AStudent() {
		this.say();
	}
	public void say() {
		System.out.println("B");
	}
	
	public static void main(String[] args) {
		new AStudent();
	}
}

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值