Java基础知识笔记(面向对象)


Java基础知识笔记(下)

面向对象

  • 面向过程与面向对象都是我们编程中,编写程序的一种思维方式
    * 面向过程的程序设计方式,是遇到一件事时,思考“我该怎么做”,然后一步步实现的过程。
    * 面向对象的程序设计方式,是遇到一件事时,思考“我该让谁来做”,然后那个“谁”就是对象,他要怎么做
    * 面向对象好处
    * a: 面向对象思维方式是一种更符合人们思考习惯的思想
    * b: 面向过程思维方式中更多的体现的是执行者(自己做事情),面向对象中更多的体现是指挥者(指挥对象做事情)。
    * c: 面向对象思维方式将复杂的问题简单化。

内存中方法调用完毕之后的小tips:

  • 栈内存中方法执行完毕之后,出栈死亡,当main方法也执行完毕之后,main方法弹栈死亡,在堆内存中new的对象也变成了垃圾。
  • 栈内存与堆内存不同,栈内存中的空间用完就释放;而栈内存中的对象或者数组,需要等着虚拟机来清理内存。

类与对象

类和对象的关系

  • 类是对某一类事物的抽象描述,而对象用于表示现实中该类事物的个体
  • 举例
    • 可以将玩具模型看作是一个类,将一个个玩具看作对象,从玩具模型和玩具之间的关系便可以看出类与对象之间的关系

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

  • 区别一:定义的位置不同
    • 定义在类中的变量是成员变量
    • 定义在方法中或者{}语句里面的变量是局部变量
  • 区别二:在内存中的位置不同
    • 成员变量存储在堆内存的对象中
    • 局部变量存储在栈内存的方法中
  • 区别三:声明周期不同
    • 成员变量随着对象的出现而出现在堆中,随着对象的消失而从堆中消失
    • 局部变量随着方法的运行而出现在栈中,随着方法的弹栈而消失
  • 区别四:初始化不同
    • 成员变量因为在堆内存中,所有默认的初始化值
    • 局部变量没有默认的初始化值,必须手动的给其赋值才可以使用。

封装

  • A.面向对象三大特征
    * 封装、继承、多态
    • B.封装表现
      • 1、方法就是一个最基本封装体
      • 2、类其实也是一个封装体
    • C.封装的好处
      • 1、提高了代码的复用性
      • 2、隐藏了实现细节,还要对外提供可以访问的方式。便于调用者的使用。这是核心之一,也可以理解为就是封装的概念
      • 3、提高了安全性

private关键字

  • A.private概述
    * private可以修饰成员内容包括成员方法和成员变量
    * 被private修饰的内容不能在其他类访问(不允许外面的类,调用自己的成员变量)
    • B.使用步骤
      • 1、通过private修饰属性
    • C.完整代码

this关键字

区分成员变量和局部变量的同名

  • 当类中存在成员变量和局部变量同名的时候为了区分,就需要使用this关键字
  • 方法中变量使用遵循就近原则
  • this:本类的对象引用

继承

继承的概述

 A:继承的概念
    a:继承描述的是事物之间的所属关系,通过继承可以使多种事物之间形成一种关系体系
   b:在Java中,类的继承是指在一个现有类的基础上去构建一个新的类,
        构建出来的新类被称作子类,现有类被称作父类
  B:继承关系的子类特点  
    a:子类会自动拥有父类所有非private修饰的属性和方法

继承的格式

    class 子类 extends 父类 {}

雇员(Employee)与研发部员工(Developer)案例:

定义员工类Employee
*/
	class Employee {
		String name; // 定义name属性
    public void work() {// 定义员工的工作方法
			System.out.println("尽心尽力地工作");
		}
	}
*Developer.java:
    /*
	 * 定义研发部员工类Developer 继承 员工类Employee
	 * 继承了父类中所有非private修饰的成员变量
	 */
	class Developer extends Employee {
		// 定义一个打印name的方法
		public void printName() {
			System.out.println("name=" + name);
		}
	}
*测试员工类与研发部员工类:
    /*
     * 定义测试类
     */
	public class Example01 {
		public static void main(String[] args) {
			Developer d = new Developer(); // 创建一个研发部员工类对象
			d.name = "小明"; // 为该员工类的name属性进行赋值
			d.printName(); // 调用该员工的printName()方法
			d.work(); // 调用Developer类继承来的work()方法
		}
	}
*通过子类对象既可以调用自身的非private修饰的成员,也可以调用父类的非private修饰的成员

继承的好处

继承的好处:
1、继承的出现提高了代码的复用性,提高软件开发效率。
2、继承的出现让类与类之间产生了关系,提供了多态的前提。

继承的注意事项

	 在Java中,类只支持单继承,不允许多继承,也就是说一个类只能有一个直接父类,
	 例如下面这种情况是不合法的。
	     class A{} 
	     class B{}
	     class C extends A,B{}  // C类不可以同时继承A类和B类

在Java中,子类和父类是一种相对概念,
也就是说一个类是某个类父类的同时,也可以是另一个类的子类。

继承的体系

继承的体系:

					                  动物(吃)
					                    |
					           -------------------------
					           |                        |
					        猫科动物(吃,胎生)      爬行动物(吃,卵生)
					           |                            |
			 -------------------------------        -----------------      
		     |          			       |        |                |
			猫(吃,抓老鼠,胎生)   虎(吃,领地,胎生)  蛇(吃,冬眠,卵生)  鳄鱼(吃,潜水,卵生)


​ 动物体系是对每个具体事物共性的抽取,子类的共性抽取形成父类
​ 父类:具有所有子类的共性内容
​ 子类:不但有共性还有自身特有的内容
​ 整个继承体系,越向上越抽象,越向下越具体

继承后子类父类成员变量的特点

子类的对象调用成员变量的时候,子类自己有,使用子类,子类自己没有调用的父类
子父类中出现了同名的成员变量时
在子类中需要访问父类中非私有成员变量时,需要使用super关键字

继承后子类父类成员方法的特性

子类重写父类方法

子类的对象调用方法的时候,子类自己有,使用子类,子类自己没有调用的父类

  class Fu{
			public void show(){
				System.out.println("Fu类中的show方法执行");
			}
		}
		class Zi extends Fu{
			public void show2(){
				System.out.println("Zi类中的show2方法执行");
			}
		}
		public  class Test{
			public static void main(String[] args) {
				Zi z = new Zi();
				z.show(); //子类中没有show方法,但是可以找到父类方法去执行
				z.show2();
			}
		}  

为什么要有重写?

 class Fu{
     	public void method(){
     		//上千行代码
            //Fu类中的方法最先存在,那么如果项目需求变了,该方法
            //功能不能够满足我们的需求,此时我们也不会去改这个方法
            //因为项目中可能有大量的功能已经使用到该方法,如果随意修改可能使调用该方法的功能出现问题
            //所以使用重写方式基于原有功能提供更强的功能
     	}   
     }
     class Zi extends Fu{
      
     }
 c:子类中出现与父类一模一样的方法时,会出现覆盖操作,也称为override重写、复写或者覆盖
   class Fu{
		public void show(){
			System.out.println("Fu show");
		}
   }
   
   class Zi extends Fu{
		//子类复写了父类的show方法
		public void show(){
			System.out.println("Zi show");
		}
	}
   public  class Test{
		public static void main(String[] args) {
			Zi z = new Zi();
			z.show(); //Zi show 子类有直接使用子类
		}
	}

Fu类中的方法最先存在,那么如果项目需求变了,
该方法功能不能够满足我们的需求,此时我们也不会去改这个方法
因为项目中可能有大量的功能已经使用到该方法,如果随意修改可能使调用该方法的功能出现问题
所以使用重写方式基于原有功能提供更强的功能
权限:子类方法覆盖父类方法,必须要保证权限大于等于父类权限。
四大权限:public>默认=protected>private

重载与重写对比

重载:

    权限修饰符(public private 默认):无关
    方法名:重载的两个方法的方法名必须相同
    形参列表:
      形参类型的顺序不同
      形参的个数不同
      形参的类型不同
      三者至少满足一个
    返回值类型:
      重载与返回值类型无关

重写:

    权限修饰符(public private 默认): 
      子类方法的权限>=父类的方法的权限
    方法名: 
      子类方法和父类方法必须相同
    形参列表: 
       子类方法和父类方法的形参列表必须相同
    返回值类型:
      基本类数据类型:
        必须相同
      引用数据类型:
       子类方法的返回值类型和父类方法的返回值类型相同
       或者
       子类方法的返回值类型是父类方法的返回值类型的 子类

抽象类

抽象类特点

A:抽象类的特点
a:抽象类和抽象方法都需要被abstract修饰。抽象方法一定要定义在抽象类中。
b:抽象类不可以直接创建对象,原因:调用抽象方法没有意义。
c:只有覆盖了抽象类中所有的抽象方法后,其子类才可以创建对象。否则该子类还是一个抽象类。
之所以继承抽象类,更多的是在思想,是面对共性类型操作会更简单。

  abstract class A{
  	public abstract void func();
  	public abstract void func2();
  }
  class A2 extends A{//A2把A中的两个抽象方法都重写掉了
  	                 //A2类不再是抽象类
  	 public void func(){}
  	 public void func2(){}
  }

抽象类一定是个父类?
是的,因为不断抽取而来的。
抽象类中是否可以不定义抽象方法?
是可以的,那这个抽象类的存在到底有什么意义呢?不让该类创建对象,方法可以直接让子类去使用
(适配器设计模式)

/*
		 *   抽象类,可以没有抽象方法,可以定义带有方法体的方法
		 *   让子类继承后,可以直接使用
		 */
		public  abstract class Animal {
		     public void sleep(){
		    	 System.out.println("动物睡觉");
		     }
		     }
	public class Cat extends Animal{

    }     
    
    public class Test {
		public static void main(String[] args) {
			//Cat c = new Cat();
			new Cat().sleep();//不让该类创建对象,方法可以直接让子类去使用
		}
    }

抽象关键字abstract不可以和哪些关键字共存?
1:private:私有的方法子类是无法继承到的,也不存在覆盖,
而abstract和private一起使用修饰方法,abstract既要子类去实现这个方法,
而private修饰子类根本无法得到父类这个方法。互相矛盾。

接口

接口概念

接口是功能的集合,同样可看做是一种数据类型,是比抽象类更为抽象的”类”。
接口只描述所应该具备的方法,并没有具体实现,具体的实现由接口的实现类(相当于接口的子类)来完成。这样将功能的定义与实现分离,优化了程序设计。
请记住:一切事物均有功能,即一切事物均有接口。

接口定义

与定义类的class不同,接口定义时需要使用interface关键字。
定义接口所在的仍为.java文件,虽然声明时使用的为interface关键字的编译后仍然会产生.class文件。这点可以让我们将接口看做是一种只包含了功能声明的特殊类。

public interface 接口名 {
			抽象方法1;
			抽象方法2;
			抽象方法3;
		}

定义步骤
使用interface代替了原来的class,其他步骤与定义类相同:
接口中的方法均为公共访问的抽象方法
接口中无法定义普通的成员变量

接口的实现类

类与接口的关系

类与接口的关系为实现关系,即类实现接口。实现的动作类似继承,只是关键字不同,实现使用implements。
其他类(实现类)实现接口后,就相当于声明:”我应该具备这个接口中的功能”。实现类仍然需要重写方法以实现具体的功能。

实现格式

classimplements 接口 {
		重写接口中方法
	} 

实现类注意事项

  • 在类实现接口后,该类就会将接口中的抽象方法继承过来,此时该类需要重写该抽象方法,完成具体的逻辑。
  • 接口中定义功能,当需要具有该功能时,可以让类实现该接口,只声明了应该具备该方法,是功能的声明。
  • 在具体实现类中重写方法,实现功能,是方法的具体实现。

接口中成员变量的特点

接口中可以定义变量,但是变量必须有固定的修饰符修饰,public static final 所以接口中的变量也称之为常量,其值不能改变。

interface Demo { ///定义一个名称为Demo的接口。
			public static final int NUM = 3;// NUM的值不能改变
		}

接口中成员方法的特点

接口中可以定义方法,方法也有固定的修饰符,public abstract
类必须覆盖掉接口中所有的抽象方法后,子类才可以实例化。否则子类是一个抽象类。

interface Demo { ///定义一个名称为Demo的接口。
			public abstract void show1();
			public abstract void show2();
		}
		
		//定义子类去覆盖接口中的方法。类与接口之间的关系是 实现。通过 关键字 implements
		class DemoImpl implements Demo { //子类实现Demo接口。
			//重写接口中的方法。
			public void show1(){}
			public void show2(){}
		}

类和接口的多实现

了解了接口的特点后,那么想想为什么要定义接口,使用抽象类描述也没有问题,接口到底有啥用呢?
接口最重要的体现:解决多继承的弊端。将多继承这种机制在java中通过多实现完成了。

  • 多实现的优点
    • 怎么解决多继承的弊端呢?
    • 弊端:多继承时,当多个父类中有相同功能时,子类调用会产生不确定性。
    • 其实核心原因就是在于多继承父类中功能有主体,而导致调用运行时,不确定运行哪个主体内容。
    • 为什么多实现能解决了呢?
    • 因为接口中的功能都没有方法体,由子类来明确。
interface Fu1{
		void show1();
	}
interface Fu2{
		void show2();
	}
	class Zi implements Fu1,Fu2 {    // 多实现。同时实现多个接口。
		public void show1(){}
		public void show2(){}
	}

类在继承类的同时实现多接口

  • 继承的同时实现接口
    • 接口和类之间可以通过实现产生关系,同时也学习了类与类之间可以通过继承产生关系。当一个类已经继承了一个父类,它又需要扩展额外的功能,这时接口就派上用场了。
    • 子类通过继承父类扩展功能,通过继承扩展的功能都是子类应该具备的基础功能。如果子类想要继续扩展其他类中的功能呢?这时通过实现接口来完成。
    • 接口的出现避免了单继承的局限性。父类中定义的事物的基本功能。接口中定义的事物的扩展功能。
 class Fu {
		public void show(){}
	}
	interface Inter {
		pulbic abstract void show1();
	}
	class Zi extends Fu implements Inter {
		public void show1() {
		}
	}

接口的多继承

 interface Fu1{
		void show();
	}
	interface Fu2{
		void show1();
	}
	interface Fu3{
		void show2();
	}
	interface Zi extends Fu1,Fu2,Fu3{
		void show3();
	}

在开发中如果多个接口中存在相同方法,这时若有个类实现了这些接口,那么就要实现接口中的方法,由于接口中的方法是抽象方法,子类实现后也不会发生调用的不确定性。

接口的好处

  • 总结:接口在开发中的它好处
    • 1、接口的出现扩展了功能。
    • 2、接口其实就是暴漏出来的规则。
    • 3、接口的出现降低了耦合性,即设备与设备之间实现了解耦。

接口和抽象类的区别

  • A: 明白了接口思想和接口的用法后,接口和抽象类的区别是什么呢?接口在生活体现也基本掌握,那在程序中接口是如何体现的呢?
    通过实例进行分析和代码演示抽象类和接口的用法。
    • B: 举例:

      • 犬:
        行为:
        吼叫;
        吃饭;
      • 缉毒犬:
        行为:
        吼叫;
        吃饭;
        缉毒;
    • 由于犬分为很多种类,他们吼叫和吃饭的方式不一样,在描述的时候不能具体化,也就是吼叫和吃饭的行为不能明确。

    • 当描述行为时,行为的具体动作不能明确,这时,可以将这个行为写为抽象行为,那么这个类也就是抽象类。

    • 可是当缉毒犬有其他额外功能时,而这个功能并不在这个事物的体系中。这时可以让缉毒犬具备犬科自身特点的同时也有其他额外功能,可以将这个额外功能定义接口中。

interface 缉毒{
		public abstract void 缉毒();
	}
	//定义犬科的这个提醒的共性功能
	abstract class 犬科{
	public abstract void 吃饭();
	public abstract void 吼叫();
	}
	// 缉毒犬属于犬科一种,让其继承犬科,获取的犬科的特性,
	//由于缉毒犬具有缉毒功能,那么它只要实现缉毒接口即可,这样即保证缉毒犬具备犬科的特性,也拥有了缉毒的功能
	class 缉毒犬 extends 犬科 implements 缉毒{
	
		public void 缉毒() {
		}
		void 吃饭() {
		}
		void 吼叫() {
		}
	}
	class 缉毒猪 implements 缉毒{
		public void 缉毒() {
		}
	}

接口和抽象类区别总结

  • 相同点:

     都位于继承的顶端,用于被其他类实现或继承;
     都不能直接实例化对象;
     都包含抽象方法,其子类都必须覆写这些抽象方法;
    
  • 区别:

     抽象类为部分方法提供实现,避免子类重复实现这些方法,提高代码重用性;接口只能包含抽象方法;
     一个类只能继承一个直接父类(可能是抽象类),却可以实现多个接口;(接口弥补了Java的单继承)
     抽象类是这个事物中应该具备的内容, 继承体系是一种 is..a关系
     接口是这个事物中的额外内容,继承体系是一种 like..a关系
    
  • 二者的选用:

     优先选用接口,尽量少用抽象类;
     需要定义子类的行为,又要为子类提供共性功能时才选用抽象类;
    

多态

多态概念

  • 多态是继封装、继承之后,面向对象的第三大特性。

     现实事物经常会体现出多种形态,如学生,学生是人的一种,
     则一个具体的同学张三既是学生也是人,即出现两种形态。	
    
  • Java作为面向对象的语言,同样可以描述一个事物的多种形态。如Student类继承了Person类,一个Student的对象便既是Student,又是Person。

  • Java中多态的代码体现在一个子类对象(实现类对象)既可以给这个子类(实现类对象)引用变量赋值,又可以给这个子类(实现类对象)的父类(接口)变量赋值。

     如Student类可以为Person类的子类。
     那么一个Student对象既可以赋值给一个Student类型的引用,也可以赋值给一个Person类型的引用。
    
  • 最终多态体现为父类引用变量可以指向子类对象。 多态的前提是必须有子父类关系或者类实现接口关系,否则无法完成多态。

  • 在使用多态后的父类引用变量调用方法时,会调用子类重写后的方法。

多态调用的三种格式

* A: 多态的定义格式:
	* 就是父类的引用变量指向子类对象
			 父类类型  变量名 = new 子类类型();
			 变量名.方法名();
* B: 普通类多态定义的格式
		父类 变量名 = new 子类();
		举例:	
			class Fu {}
			class Zi extends Fu {}
			//类的多态使用
			Fu f = new Zi();
* C: 抽象类多态定义格式			
		抽象类 变量名 = new 抽象类子类();
		举例:	
		abstract class Fu {
		         public abstract void method();
			     }
		class Zi extends Fu {
		public void method(){
				      System.out.println(“重写父类抽象方法”);
		}
		}
		//类的多态使用
		Fu fu= new Zi();
* D: 接口多态定义的格式
		接口 变量名 = new 接口实现类();
		如: interface Fu {
				     public abstract void method();
		}
		class Zi implements Fu {
				     public void method(){
		              System.out.println(“重写接口抽象方法”);
		}
		}
		//接口的多态使用
		Fu fu = new Zi();
* E: 注意事项
		同一个父类的方法会被不同的子类重写。在调用方法时,调用的为各个子类重写后的方法。
		如 Person p1 = new Student();
		   Person p2 = new Teacher();
		   p1.work(); //p1会调用Student类中重写的work方法
		   p2.work(); //p2会调用Teacher类中重写的work方法
		当变量名指向不同的子类对象时,由于每个子类重写父类方法的内容不同,所以会调用不同的方法。

多态成员的特点

* A: 掌握了多态的基本使用后,那么多态出现后类的成员有啥变化呢?
* 前面学习继承时,我们知道子父类之间成员变量有了自己的特定变化,
	* 那么当多态出现后,成员变量在使用上有没有变化呢?
	* 多态出现后会导致子父类中的成员变量有微弱的变化
	* 
	* * B: 代码演示
	class Fu {
		int num = 4;
	}
	class Zi extends Fu {
		int num = 5;
	}
	class Demo {
		public static void main(String[] args) 	{
			Fu f = new Zi();
			System.out.println(f.num);
			Zi z = new Zi();
			System.out.println(z.num);
		}
	}

* C: 多态成员变量
	当子父类中出现同名的成员变量时,多态调用该变量时:
	编译时期:参考的是引用型变量所属的类中是否有被调用的成员变量。没有,编译失败。
	运行时期:也是调用引用型变量所属的类中的成员变量。
	简单记:编译和运行都参考等号的左边。编译运行看左边。

* D: 多态出现后会导致子父类中的成员方法有微弱的变化。看如下代码
	class Fu {
		int num = 4;
		void show()	{
			System.out.println("Fu show num");
		}
	}
	class Zi extends Fu {
		int num = 5;
		void show()	{
			System.out.println("Zi show num");
		}
	}
	class Demo {
		public static void main(String[] args) 	{
			Fu f = new Zi();
			f.show();
		}
	}

* E: 多态成员方法
	编译时期:参考引用变量所属的类,如果没有类中没有调用的方法,编译失败。
	运行时期:参考引用变量所指的对象所属的类,并运行对象所属类中的成员方法。
	简而言之:编译看左边,运行看右边。
  • 多态中,成员特点
  • 成员变量:
  • 编译的时候, 参考父类中有没有这个变量,如果有,编译成功,没有编译失败
  • 运行的时候, 运行的是父类中的变量值
  • 编译运行全看父类
  • 成员方法:
  • 编译的时候, 参考父类中有没有这个方法,如果有,编译成功,没有编译失败
  • 运行的时候, 运行的是子类的重写方法
  • 编译看父类,运行看子类

instanceof关键字

* A: 作用
	 可以通过instanceof关键字来判断某个对象是否属于某种数据类型。
	 如学生的对象属于学生类,学生的对象也属于人类

* 格式:
	boolean  b  = 对象  instanceof  数据类型;

* 举例:
	Person p1 = new Student(); // 前提条件,学生类已经继承了人类
	boolean flag = p1 instanceof Student; //flag结果为true
	boolean flag2 = p2 instanceof Teacher; //flag结果为false

多态-向上转型

向上转型:当有子类对象赋值给一个父类引用时,便是向上转型,多态本身就是向上转型的过程。
	使用格式:
	父类类型  变量名 = new 子类类型();
	如:Person p = new Student();

多态-向下转型

向下转型:一个已经向上转型的子类对象可以使用强制类型转换的格式,将父类引用转为子类引用,这个过程是向下转型。
如果是直接创建父类对象,是无法向下转型的!
使用格式:
子类类型 变量名 = (子类类型) 父类类型的变量;
如:Student stu = (Student) p; //变量p 实际上指向Student对象

多态的好处和弊端

	* A: 多态的好处和弊端
		* 当父类的引用指向子类对象时,就发生了向上转型,即把子类类型对象转成了父类类型。
		  向上转型的好处是隐藏了子类类型,提高了代码的扩展性。
		* 但向上转型也有弊端,只能使用父类共性的内容,而无法使用子类特有功能,功能有限制。
	* B: 看如下代码
	//描述动物类,并抽取共性eat方法
	abstract class Animal {
		abstract void eat();
	}
	 
	// 描述狗类,继承动物类,重写eat方法,增加lookHome方法
	class Dog extends Animal {
		void eat() {
			System.out.println("啃骨头");
		}
	
		void lookHome() {
			System.out.println("看家");
		}
	}
	
	// 描述猫类,继承动物类,重写eat方法,增加catchMouse方法
	class Cat extends Animal {
		void eat() {
			System.out.println("吃鱼");
		}
	
		void catchMouse() {
			System.out.println("抓老鼠");
		}
	}
	
	public class Test {
		public static void main(String[] args) {
			Animal a = new Dog(); //多态形式,创建一个狗对象
			a.eat(); // 调用对象中的方法,会执行狗类中的eat方法
			// a.lookHome();//使用Dog类特有的方法,需要向下转型,不能直接使用
			
			// 为了使用狗类的lookHome方法,需要向下转型
	// 向下转型过程中,可能会发生类型转换的错误,即ClassCastException异常
			// 那么,在转之前需要做健壮性判断 
			if( !a instanceof Dog){ // 判断当前对象是否是Dog类型
			 		System.out.println("类型不匹配,不能转换"); 
			 		return; 
			} 
			Dog d = (Dog) a; //向下转型
			d.lookHome();//调用狗类的lookHome方法
		}
	} 
	* C 多态总结:
	什么时候使用向上转型:
		当不需要面对子类类型时,通过提高扩展性,或者使用父类的功能就能完成相应的操作,这时就可以使用向上转型。
		如:Animal a = new Dog();
		    a.eat();
	什么时候使用向下转型
		当要使用子类特有功能时,就需要使用向下转型。
			如:Dog d = (Dog) a; //向下转型
			    d.lookHome();//调用狗类的lookHome方法
			向下转型的好处:可以使用子类特有功能。
			弊端是:需要面对具体的子类对象;在向下转型时容易发生ClassCastException类型转换异常。在转换之前必须做类型判断。
		如:if( !a instanceof Dog){}

构造方法

构造方法的作用

在new的同时给成员变量赋值,给对象属性进行初始化。

Perons p = new Person("张三",23); 

在new 的时候给p对象的name属性和age属性进行赋值,使这个对象的属性有值。

构造方法定义

A: 构造方法定义
构造方法的格式:
		修饰符 构造方法名(参数列表)
		{
		}
	
* B: 构造方法的体现:
		构造方法没有返回值类型。也不需要写返回值。因为它是为构建对象的,对象创建完,方法就执行结束。
		构造方法名称必须和类型保持一致。
		构造方法没有具体的返回值。
		构造方法的代码体现:

* C: 构造方法举例
		class Person {
			// Person的成员属性age和name
			private int age;
			private String name;
		
			// Person的构造方法,拥有参数列表
			Person(int a, String nm) {
				// 接受到创建对象时传递进来的值,将值赋给成员属性
				age = a;
				name = nm;
			}
		}

* D: 构造方法运行特点:new 对象的时候自动调用执行。

默认添加的构造方法

  • A: 每一class类都必须有一个构造方法,构造方法不写也有。
    编译的时候,javac,系统会自动检查类中是否有构造方法,如果没有编译器就会自动添加一个构造方法
    比如Person类, 编译器添加一个无参构造 public Person(){}

构造方法的调用赋值

* A: 理解构造方法的格式和基本功能之后,现在就要研究构造方法是怎么执行的呢?
* 在创建对象的时候是如何初始化的呢?
		 构造方法是专门用来创建对象的,也就是在new对象时要调用构造方法。
		 现在来看看如何调用构造方法。
* B: 案例
		class Person {
			// Person的成员属性age和name
			private int age;
			private String name;
		
			// Person的构造方法,拥有参数列表
			Person(int a, String nm) {
				// 接受到创建对象时传递进来的值,将值赋给成员属性
				age = a;
				name = nm;
			}
		
			public void speak() {
				System.out.println("name=" + name + ",age=" + age);
			}
		}
		
		class PersonDemo {
			public static void main(String[] args) {
				// 创建Person对象,并明确对象的年龄和姓名
				Person p2 = new Person(23, "张三");
				p2.speak();
			}
		}

	上述代码演示了创建对象时构造方法的调用。即在创建对象时,会调用与参数列表对应的构造方法

构造方法的内存

在这里插入图片描述
内存加载的过程
有一个Person类, 创建Person 对象new Person()
1、首先会将main方法压入栈中,执行main方法中的 new Person(23,“张三”);
2、在堆内存中分配一片区域,用来存放创建的Person对象,这片内存区域会有属于自己的内存地址(0x88)。然后给成员变量进行默认初始化(name=null,age=0)。
3、执行构造方法中的代码(age = a ; name = nm;),将变量a对应的23赋值给age,将变量nm对应的”张三赋值给name,这段代码执行结束后,成员变量age和name的值已经改变。执行结束之后构造方法弹栈,Person对象创建完成。将Person对象的内存地址0x88赋值给p2。

构造方法的重载

* A:当在描述事物时,要不要在类中写构造方法呢?
* 这时要根据描述事物的特点来确定,当描述的事物在创建其对象时就要明确属性的值,
* 这时就需要在定义类的时候书写带参数的构造方法。
	*    若创建对象时不需要明确具体的数据,这时可以不用书写构造方法(不书写也有默认的构造方法)。
			构造方法的细节:
			1、一个类中可以有多个构造方法,多个构造方法是以重载的形式存在的
			2、构造方法是可以被private修饰的,作用:其他程序无法创建该类的对象。
	* B: 举例
		class Person {
			private int age;
			private String name;
			// 私有无参数的构造方法,即外界不能通过new Person();语句创建本类对象
		private Person() {
		}
	
		// 多个构造方法是以重载的形式存在
		Person(int a) {
			age = a;
		}
	
		Person(String nm, int a) {
			name = nm;
			age = a;
		}
	}

构造方法和一般方法区别

* A: 目前为止,学习两种方法,分别为构造方法和一般方法,那么他们之间有什么异同呢?
	1.格式不同
	 构造方法 : 修饰符  类名(参数类型 参数 ...){
		初始化成员变量
	}
	一般方法: 需要有返回值类型
	2.作用不同
	构造方法一般用来给成员变量初始化;
	一般方法根据需求而定;
	3.调用方式不同
	构造方法创建对象时调用, 或者this() super() 语句调用
	普通方法需要对象调用或者静态方法直接调用静态方法.
	4.执行不同
	构造方法在对象创建时就执行了,而且只执行一次。
	一般方法是在对象创建后,需要使用时才被对象调用,并可以被多次调用。

this在构造方法之间的调用

* A: 在之前学习方法之间调用时,可以通过方法名进行调用。
    * 可是针对构造方法,无法通过构造方法名来相互调用。
		构造方法之间的调用,可以通过this关键字来完成。
		构造方法调用格式:
		this(参数列表);
* B:调用构造方法的案例
	class Person {
		// Person的成员属性
		private int age;
		private String name;
	
		// 无参数的构造方法
		Person() {
		}
	
		// 给姓名初始化的构造方法
		Person(String nm) {
			name = nm;
		}
	
		// 给姓名和年龄初始化的构造方法
		Person(String nm, int a) {
			// 由于已经存在给姓名进行初始化的构造方法 name = nm;因此只需要调用即可
			// 调用其他构造方法,需要通过this关键字来调用
			this(nm);
			// 给年龄初始化
			age = a;
		}
	}

super关键字

  • A: 子父类中构造方法的调用
    在创建子类对象时,父类的构造方法会先执行,因为子类中所有构造方法的第一行有默认的隐式super();语句。
  • B: 格式:
    调用本类中的构造方法
    this(实参列表);
    调用父类中的空参数构造方法
    super();
    调用父类中的有参数构造方法
    super(实参列表);
* A:子类构造方法,有一个默认添加的构造方法
		public class Student extends Person {
			 public Student(){
			 	super();
			 }
		}
	* B :为什么子类对象创建都要访问父类中的构造方法?
	* 因为子类继承了父类的内容,所以创建对象时,必须要先看父类是如何对其内容进行初始化的,看如下程序
		public class Test {
			public static void main(String[] args) {
				new Zi();
			}
				}
	class Fu{
		int num ;
		Fu(){
			System.out.println("Fu构造方法"+num);
			num = 4;
		}
	}
	class Zi extends Fu{
		Zi(){
	         //super(); 调用父类空参数构造方法
			System.out.println("Zi构造方法"+num);
		}
	}
执行结果:
 Fu构造方法0
 Zi构造方法4
 通过结果发现,子类构造方法执行时中,调用了父类构造方法,这说明,子类构造方法中有一句super()。
	那么,子类中的构造方法为什么会有一句隐式的super()呢?
	原因:子类会继承父类中的内容,所以子类在初始化时,必须先到父类中去执行父类的初始化动作。
	这样,才可以使用父类中的内容。
	当父类中没有空参数构造方法时,子类的构造方法必须有显示的super语句,指定要访问的父类有参数构造方法。
       
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值