尚硅谷Java学习笔记-基础部分-5

前言
记录没有条理性,记一些自己没怎么用过,但是比较重要的内容。所以不是那么全面。
因为自己有一些java基础,为秋招准备的。大家看看就好,有错误的地方欢迎指正。

本节内容:面向对象-中,继承,多态,重写,super,单元测试,包装类等。

一、继承性

优点:代码的复用,功能的拓展,多态性的前提
格式:class A extends B{},A为子类(sub),B为父类(super、基类)

1、注意事项
  • 子类继承父类中的所有属性和方法,直接拿过来即可用,不用重新定义。父类中的private方法子类可以继承,但是不能直接使用。(继承性不能打破封装性)
  • 子类继承父类后,还可以自己拓展新的功能。
  • 一个类只有一个父类,一个父类可以有多个子类,子类继承父类,也同时继承爷爷类的方法。
  • object类:如没有特殊设定,类默认继承于java.lang.Object类,也具有它的属性和功能。即所有的类都是其子孙。
    在这里插入图片描述
    在这里插入图片描述
//person.java
package com.atguigu.java;

public class Person extends Creature{  //前面creature类中只有breathe方法	
	String name;
	private int age;
	
	public Person(){
	}
	public Person(String name,int age){
		this.name = name;
		this.age = age;
	}
	
	public void eat(){
		System.out.println("吃饭");
		sleep(); //调用下面sleep方法
	}
	private void sleep(){
		System.out.println("睡觉");
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
}
//同package下的student.java
package com.atguigu.java;

public class Student extends Person{  //继承于person,拿下他的属性和方法
	
//	String name; //所以不用再次声明
//	int age;
	String major;
	
	public Student(){	
	}
	public Student(String name,int age,String major){
		this.name = name;
//		this.age = age;
		setAge(age);
		this.major = major;
	}
//	public void eat(){
//		System.out.println("吃饭");
//	}
//	
//	public void sleep(){
//		System.out.println("睡觉");
//	}
	
	public void study(){  //student自己独特的方法。
		System.out.println("学习");
	}
	public void show(){
		System.out.println("name:" + name + ",age:" + getAge());
	}
	
}
番外:eclipse的debug功能
代码量小时,可以直接sysout输出。
双击左侧加断点,右键dubug as即为调试界面,变量在断点处时存储的值。
step over就是一行一行代码地往下走,左侧箭头所指的这一行还没有执行,只有再点一次箭头到下一行,本行才执行完毕。
step into即为进入两行代码间,即进入使用的方法体内部,看看方法。droptoframe跳到该方法的最前端。
step return即为出来,和into相反。
2、方法的重写
  • override或者overwrite:子类继承父类后,对父类同名同参数的方法,进行覆盖。 重写后,创建子类对象后,子类调用的方法就是被重写后的方法了,就不是父类中的方法了。
  • 规则:
    • 书写格式:
      权限public +返回类型int +方法名getAge(形参列表) + throws异常{
      // 方法体
      } 对上述的4个要素下面详细讨论如何重写
    • 子类重写的方法a,父类中被重写的方法A
      • A和a的方法名形参列表需要相同
      • a的权限修饰符不小于A,public>缺省>protected>private。父类在这几个中往后面选,子类多往前面选。若不遵守,则调用的方法还是父类的方法而不是重写的方法。
      • 特例:父类的private不能重写,即父类的方法为private时,不可商量,不容重写。
    • 返回值类型
      • A中的void,那么a只能是void;
      • A中的某引用类型,那么a的返回可以是该引用类型或其子类;
      • A中基本数据类型,那么a中也必须相同;
      • a中抛出的异常不能大于A抛出的异常
      • Aa中方法要么都static,要么都不是
  • 重写和重载的区别
    • 重载:不同的函数使用相同的函数名,但是函数的参数个数不同。调用的时候根据函数的参数来区别不同的函数。 不表现为多态性
    • 重写:子类继承父类后,对父类同名同参数的方法,进行覆盖。表现为多态性
3、super

父类的。

  • 在子类中会重写父类的方法,但是偶尔想用一些原来的父类的方法,为了区分,使用super。
  • 可以用来调用的结构:属性、方法、构造器。下面一一解释
  • super调用属性、方法
    • 我们可以在子类的方法或构造器中。通过使用"super.属性"或"super.方法"的方式,显式的调用父类中声明的属性或方法。但是,通常情况下,我们习惯省略"super."
    • 特殊情况:当子类和父类中定义了同名的属性时,我们要想在子类中调用父类中声明的属性,则必须显式的使用"super.属性"的方式,表明调用的是父类中声明的属性。
    • 特殊情况:当子类重写了父类中的方法以后,我们想在子类的方法中调用父类中被重写的方法时,则必须显式的使用"super.方法"的方式,表明调用的是父类中被重写的方法。
  • super调用构造器
    • 我们可以在子类的构造器中显式的使用"super(形参列表)"的方式,调用父类中声明的指定的构造器,"super(形参列表)"的使用,必须声明在子类构造器的首行!
    • 我们在类的构造器中,针对于"this(形参列表)"或"super(形参列表)"只能二一,不能同时出现
    • 在构造器的首行,没显式的声明"this(形参列表)“或"super(形参列表)”,则默认调用的是父类中空参的构造器:super()
    • 在类的多个构造器中,至少一个类的构造器中使用了"super(形参列表)",调用父类中的构造器
package com.atguigu.java3;
//在本包下还有一个people.java文件

public class Student extends Person{
	
	String major;
	int id = 1002;//学号
	
	public Student(){
		super();  //父类构造器
	}
	public Student(String major){
		super();  //构造器外加新构造器,都需要先super()或者this()一下
		this.major = major;
	}
	
	public Student(String name,int age,String major){
//		this.name = name;
//		this.age = age;
		super(name,age);
		this.major = major;
	}
	
	@Override
	public void eat() {
		System.out.println("学生:多吃有营养的食物");
	}
	
	public void study(){
		System.out.println("学生:学习知识");
		this.eat(); //本类中的eat方法
		super.eat(); //父类中的eat方法
		walk(); //父类中继承过来的walk方法
	}
	
	public void show(){
		System.out.println("name = " + name + ", age = " + age);
		System.out.println("id = " + this.id);
		System.out.println("id = " + super.id);
	}
}
4、子类对象实例化过程

理解即可。
1.从结果上看:继承性
子类继承父类以后,就获取了父类中声明的属性或方法。
创建子类的对象,在堆空间中,就会加载所父类中声明的属性。
2.从过程上看:
当我们通过子类的构造器创建子类对象时,我们一定会直接或间接的调用其父类的构造器,进而调用父类的父类的构造器,…直到调用了java.lang.Object类中空参的构造器为止。正因为加载过所的父类的结构,所以才可以看到内存中父类中的结构,子类对象才可以考虑进行调用。
在这里插入图片描述
强调:虽然创建子类对象时,调用了父类的构造器,但是自始至终就创建过一个对象,即为new的子类对象。如下图。
在这里插入图片描述

二、多态性

1、概念

可以理解为一个事物的多种形态。对象的多态性表现为父类的引用指向子类的对象。
比如Person p = new Man();,在栈空间的是p(父类),指向的却是堆空间的Man()对象(子类)。

2、多态的使用

在编译期,只能调用父类声明的方法;运行期,实际执行的是子类的重写。编译看左边,运行看右边。所以先有类的继承关系和方法的重写,才可能有多态。
注意:对象的多态性,只适用于方法,不适用于属性。
看下面例子,两个代码,man继承person类。

//person.java
package com.atguigu.java4;

public class Person {
	String name;
	int age;
	
	int id = 1001;
	
	public void eat(){
		System.out.println("人:吃饭");
	}
	
	public void walk(){
		System.out.println("人:走路");
	}
	
}

//man.java
package com.atguigu.java4;
public class Man extends Person{
	
	boolean isSmoking;
	
	int id = 1002;
	
	public void earnMoney(){
		System.out.println("男人负责挣钱养家");
	}
	
	public void eat(){
		System.out.println("男人多吃肉,长肌肉");
	}
	
	public void walk(){
		System.out.println("男人霸气的走路");
	}

}
package com.atguigu.java4;

public class PersonTest {
	public static void main(String[] args) {
		
		Person p1 = new Person();
		p1.eat();
		
		Man man = new Man();
		man.eat();
		man.age = 25;
		man.earnMoney();
		
		//*************************************************
		System.out.println("*******************");
		//对象的多态性:父类的引用指向子类的对象
		Person p2 = new Man(); //内存中实际上加载了子类特有的属性和方法,但是变量被声明为父类,所以只能调用父类的属性和方法
//		Person p3 = new Woman();
		//多态的使用:当调用子父类同名同参数的方法时,实际执行的是子类重写父类的方法 ---虚拟方法调用
		p2.eat();
		p2.walk();
		
//		p2.earnMoney(); //p2是person类,他无法调用man新加的方法
		
		System.out.println(p2.id);//1001 //Person中的非Man中的
		
	}
}

所以综上,Person p2 = new Man(),p2是Person类型变量

  • p2可以调用Person里面声明过的方法,但是实际用的时候是用的子类重写过的
  • p2不可以调子类Man中新加的,Person中没有的方法
  • p2调用的父子同名属性是父类中的属性,而非子类中的属性,编译运行都在左边。
  • 所谓-编译看左边-是指在写代码的时候,如果写了p2.子类继承的某方法,点进去找到的是父类的方法,但是执行的时候还是子类重写过的方法。被重写的方法称为虚拟方法
3、多态的用处:抽象类,接口
package com.atguigu.java4;

import java.sql.Connection;

//多态性的使用举例一:
public class AnimalTest {
	
	public static void main(String[] args) {
		
		AnimalTest test = new AnimalTest();  //自己new自己,调自己的方法,main作为入口
		test.func(new Dog());  //匿名对象dog,同Dog d = new Dog()。这里匿名对象传入下面func的方法作为形参。因为eat和shout方法被重写,所以执行的不是animal的方法,而是dog重写后的方法。//狗吃骨头,汪汪汪
		test.func(new Cat()); //执行猫重写后的方法 //猫吃鱼,喵喵喵
	}
	
	public void func(Animal animal){//Animal animal = new Dog();
		animal.eat();
		animal.shout();
		
		if(animal instanceof Dog){
			Dog d = (Dog)animal;
			d.watchDoor();
		}
	}
	
	//如果没有多态性,就只能造很多重载的方法
//	public void func(Dog dog){
//		dog.eat();
//		dog.shout();
//	}
//	public void func(Cat cat){
//		cat.eat();
//		cat.shout();
//	}
}


class Animal{

	public void eat(){
		System.out.println("动物:进食");
	}
	
	public void shout(){
		System.out.println("动物:叫");
	}
	
}

class Dog extends Animal{  // dog类,继承于animal,有重写eat,shout新建watchdoor方法
	public void eat(){
		System.out.println("狗吃骨头");
	}
	
	public void shout(){
		System.out.println("汪!汪!汪!");
	}
	
	public void watchDoor(){
		System.out.println("看门");
	}
}
class Cat extends Animal{ //cat类,继承于animal,有重写eat和shout方法
	public void eat(){
		System.out.println("猫吃鱼");
	}
	
	public void shout(){
		System.out.println("喵!喵!喵!");
	}
}

//举例二:

class Order{
	
	public void method(Object obj){ //形参是object,不管来什么对象,都有对应的重写过的方法,这样的话就不用一一定义各自的方法了。
	
	}
}

//举例三:
class Driver{
	//或者后面会有数据库,有mysql,oracle,db2,sqlserver,想用java连接并操作数据,在这之前需要先连接。Connection是父类,这里传入的不同数据库,都能有对应的方法
	public void doData(Connection conn){//conn = new MySQlConnection(); / conn = new OracleConnection();
		//规范的步骤去操作数据
//		conn.method1(); //不同的数据库对应不同数据库的方法
//		conn.method2();
//		conn.method3();
		
	}
	
}
4、多态周边:
  • 上面,Person p2 = new Man(),p2为Person类型变量,可以调用父类的属性(父类的)和方法(被子类重写过的),无法调用子类特有的属性和方法。
  • Man m1 = p2报错,左右类型不一样,可以Man m1 = (Man)p2向下转型。但是可能出现classcastexception错误,比如p2定义为Woman w1 = (Woman)p1p1指向的是Man,无法再定义为woman,定义错误。
  • 引出a instanceof A,判断对象a是否是A的实例。如果不会出现classcast报错的话,instanceof返回true。比如
	Person p2 = new Man();
	p2 instanceof Man:True; 
	p2 instanceof Woman: False;
	p2 instanceof Person: True;

	//编译ok,运行不通过
	Person p3 = new Woman();
	Man m3 = (Man) p3 //女人变男人
	Person p4 = new Person();
	Man m4 = (Man) p4; //人转男人

	//编译运行皆ok
	Object obj = new Woman();
	Person p = (Person) obj;

	//编译不通过
	Man m5 = new Woman();
	String str = new Date();
	Object o = new Date();
	string str1 = (String) o;


在这里插入图片描述

5、多态面试题

1、谈谈你对多态性的理解:

  • 实现代码通用性;
  • Object类中定义的public boolean equals(Object obj){ }
    JDBC:使用java程序操作(获取数据库连接、CRUD)数据库(MySQL、Oracle、DB2、SQL Server),不同的数据库作为形参放进去了就会有不同的方法对应上
  • 抽象类、接口的使用肯定体现了多态性。(抽象类、接口不能实例化)

2、多态是编译时行为还是运行时行为:运行时。

三、Object类的使用

1、java.lang.Object类的说明

是所有Java类的父类;类的声明中未使用extends关键字的,默认继承与Object类;
属性:无;
方法:equals() / toString() / getClass() / hashCode() / clone() / finalize() / wait() / notify() / notifyAll()
构造器:只一个空参构造器

2、equals()方法

只适用于引用数据类型

//比较两个对象的地址值是否相同,即两个引用是否指向同一个对象实体
public boolean equals(Object obj){
	return (this == obj)
	} //地址都相同,内容也一定相同

String,Data,File,包装类都重写了该equal()方法,所以功能不再是比较两个引用的地址是否相同,而是比较两个对象的实体(地址里存的值)是否相同。
通常情况下,我们自己写的类都会重新继承Object类,所以一般都需要重写。重写举例,以下代码是基本功,要求会默写

class User{
	String name;
	int age;
	public boolean equals(Object obj){
		if(obj == this){
			return true;
		} //要是地址都一样,就不用比了,直接true
		if(obj instanceof User){ //如果对象类型都不同,就直接到最后的false
			User u = (User)obj;
			 //一一比较属性,直接放在return后面
			return this.age == u.age && this.name.equals(u.name);
		}
		return false;
}
  • 面试题 ==: 可以比较基本数据类型,也可以比较引用数据类型。
    基本数据类型比较的是值;引用数据类型比较的是对象的地址值。使用时需要保证左右两边的变量类型一致。
    Object类中的equals和 ==效果是相同的,但是其他的一些类String中重写后的equals()则比的是地址里的内容而不是地址值了。
3、toString()的使用
  • 功能:返回类名和他的引用地址。(可以看一下Object中的定义源码)
public String toString(){
	return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
  • 当输出一个对象的引用时,实际上调用的是当前对象的toString(),也即 syso(obj.toString())和syso(obj)输出结果一样,因为system.out.println中调用了toString方法。
  • 像String,Date,File,包装类都重写了Object类中的toString()方法,返回的是地址里的内容而不是地址了
  • 自己重写该方法,返回对象的实体内容,如下
//自动实现,窗口source - generate toString - 选择关心的属性field还是方法method
	@Override
	public String toString() {
		return "Customer [name=" + name + ", age=" + age + "]";
	}

四、单元测试

  • 步骤:

  • 1.当前工程day14 - 右键选择:build path - add libraries - JUnit 4 - 下一步,生成一个软件库的包供测试调用。

  • 2.创建Java包和类,JUnitTest.java,进行单元测试。
    此时的Java类要求:① 此类是public的 ②此类提供公共的无参的构造器

  • 3.此类中声明单元测试方法。
    此时的单元测试方法:方法的权限是public,没返回值,没形参

  • 4.此单元测试方法上需要声明注解:@Test,并在单元测试类JUnitTest.java中导入:import org.junit.Test;

  • 5.声明好单元测试方法以后,就可以在方法体内测试相关的代码。写完代码以后,先左键双击单元测试方法名,右键:run as - JUnit Test

  • 说明:
    1.如果执行结果没任何异常:绿条
    2.如果执行结果出现异常:红条

package com.atguigu.java2;

import java.util.Date;
import org.junit.Test;

public class JUnitTest {  // 测试类需要是public的,并且有无参构造器,这里默认继承Object,所以有
   int num = 10; //测试类也可以有自己的属性,并且调用该属性时,不像main中需要先创建对象再用,可以直接用-见下面的show函数里面
   
   @Test
   public void testEquals(){   //注意命名方式,testXXX测试某某某
   	String s1 = "MM";
   	String s2 = "MM";
   	System.out.println(s1.equals(s2)); 
   	
   	//ClassCastException的异常
//		Object obj = new String("GG");
//		Date date = (Date)obj;
   	
   	System.out.println(num);
   	show();  // 可以直接调用方法,不用实例化出一个对象调用
   }
   
   public void show(){
   	num = 20;
   	System.out.println("show()....");
   }
   
   @Test
   public void testToString(){  //左键再右键,就可以只测试这一个方法里面的代码了,就不用全部整个java文件测试了
   	String s2 = "MM";
   	System.out.println(s2.toString());
   }
}

实际开发中,只需要在某个java文件(即某一个public类)中
@Test
public void test1{ … }
这个时候Test报错,放上鼠标就直接自动添加JUnit包

五、包装类

也叫Wrapper,封装类。针对八种基本数据类型定义相应的引用数据类型。

  • 下图中,除了int和char之外,其他的包装类都是直接把首字符大写即可。虚框中为数值型的,父类为Number
    在这里插入图片描述
    作用:比如之前的equals()函数,虽然理论上object类型的都可以传入,但是像int就传不进去。所以就有将基本数据类型放进Integer类中,这样就可以传进去了,才是真正的面向对象。
  • 三种类型的转换:基本数据类型,String,包装类之间的转换。
    在这里插入图片描述
public class Wrapper(){
	
	//基本数据类型-->包装类
	@Test
	public void test1(){
		int num1 = 10;
		System.out.println(num1.toString()); //报错,因为num1是基本数据类型,不是某个对象,不能放入println函数中作为形参
		Integer in1 = new Integer(num1); //先将基本数据类型作为形参传入Integer中
		System.out.println(in1.toString());//10,Integer中的tostring被重写过了,输出地址中的值,所以输出10
		
		Integer in2 = new Integer("123")
		System.out.println(in2.toString());//123
		
		Integer in3 = new Integer("123abc")
		System.out.println(in1.toString());//报错,有非数字的内容

		Float f1 = new Float("12.3");
		Float f2 = new Float(12.3);
		Float f3 = new Float(12.3f);
		System.out.println(f1);//没有toString时,对应的重载函数会自动加toString,效果一样,输出f1,f1和f2和f3效果一样。

		Boolean b1 = new Boolean(true); //true
		Boolean b1 = new Boolean("true"); //true
		Boolean b1 = new Boolean("True"); //true
		Boolean b1 = new Boolean("true123"); //false

	// 包装类转基本数据类型,调用包装类的xxxValue(),比如一些计算,包装类无法但是基本类型可以
	@Test
	public void test2(){
		Integer in1 = new Integer(12);
		int i1 = in1.intValue();
		syso(i1+1) // 13
		
	//新特性:自动装箱和自动拆箱
	@Test
	public void test3(){
		int num1 = 10;
		//基本数据类型-->包装类的对象
		method(num1);
		
		//自动装箱:基本数据类型 --->包装类
		int num2 = 10;
		Integer in1 = num2;//自动装箱,不用再new一个空间了
		
		boolean b1 = true;
		Boolean b2 = b1;//自动装箱,直接传即可
		
		//自动拆箱:包装类--->基本数据类型
		System.out.println(in1.toString());
		
		int num3 = in1;//自动拆箱,不用intValue了
		
	}
	
	public void method(Object obj){
		System.out.println(obj);
	}
	//String类型 --->基本数据类型、包装类:调用包装类的parseXxx(String s)
	@Test
	public void test5(){
		String str1 = "123";
		//错误的情况:
//		int num1 = (int)str1;
//		Integer in1 = (Integer)str1;
		//可能会报NumberFormatException
		int num2 = Integer.parseInt(str1);
		System.out.println(num2 + 1);
		
		String str2 = "true1";
		boolean b1 = Boolean.parseBoolean(str2);
		System.out.println(b1);//false
	}
	
	//基本数据类型、包装类--->String类型:调用String重载的valueOf(Xxx xxx)
	@Test
	public void test4(){
		
		int num1 = 10;
		//方式1:连接运算
		String str1 = num1 + "";
		//方式2:调用String的valueOf(Xxx xxx)
		float f1 = 12.3f;
		String str2 = String.valueOf(f1);//"12.3"
		
		Double d1 = new Double(12.4);
		String str3 = String.valueOf(d1);
		System.out.println(str2);
		System.out.println(str3);//"12.4",输出的是字符型
		
	}

综上:

  • 基本数据类型<—>包装类:JDK 5.0 新特性:自动装箱 与自动拆箱
  • 基本数据类型、包装类—>String:调用String重载的valueOf(Xxx xxx)
  • String—>基本数据类型、包装类:调用包装类的parseXxx(String s)
    注意:转换时,可能会报NumberFormatException

面试题

public class InterviewTest {

	@Test
	public void test1() {
		Object o1 = true ? new Integer(1) : new Double(2.0);
		System.out.println(o1);// 1.0 ,:左右的类型一样,所以左侧的integer自动类型提升,所以是double型。

	}

	@Test
	public void test2() {
		Object o2;
		if (true)
			o2 = new Integer(1);
		else
			o2 = new Double(2.0);
		System.out.println(o2);// 1,各论各的,所以输出1

	}

	@Test
	public void test3() {
		Integer i = new Integer(1);
		Integer j = new Integer(1);
		System.out.println(i == j);//false,==符号比得是地址,
		
		//Integer内部定义了IntegerCache结构,IntegerCache中定义了Integer[],
		//保存了从-128~127范围的整数。如果我们使用自动装箱的方式,给Integer赋值的范围在-128~127范围内时,可以直接使用数组中的元素,不用再去new了。目的:提高效率
		//即超过这个范围的,都是需要重新new的,地址不同。在范围之内的,就保存在cache数组里,地址相同
		
		
		Integer m = 1;
		Integer n = 1;
		System.out.println(m == n);//true,自动装箱且在范围内

		Integer x = 128;//相当于new了一个Integer对象
		Integer y = 128;//相当于new了一个Integer对象
		System.out.println(x == y);//false,自动装箱且在范围外
	}

}

应用场景

  • Vector类中中添加元素,要变为形参传入addElement方法,思路:基本类型-包装类-多态(使用该数据类型对应的方法)
番外篇
  • 快捷键 ctrl alt 下键:复制上面一段选中区域到下面
  • 同一个包下,如果类中的属性权限是缺省的,则可以各个java文件之间相互调,public也是,private就不是了,只能java文件内部。

博学而笃志,切问而近思

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值