Java基础记录(三)

此博客为看B老师Java视频的部分记录
  1. 面向对象三大特征之继承
  • 好处: 提高了代码的复用性。让类和类产生了关系,给另一个特征 多态 提供了前提

  • 弊端:打破封装性

  • 使用 extends 关键字,如 class Student extends Person
    学生继承了Person, Person是父类(基类,超类),学生是子类

  • Java中继承的体现:
    Java允许单继承,不直接支持多继承,将多继承进行其他方式的体现
    单继承:一个子类只能有一个父类
    多继承:一个子类可以有多个父类
    Java还支持多重继承,形成继承体系,如:
    在这里插入图片描述
    C也可以调用A中东西

  • 补充

    • 子类不可以直接访问父类中private内容
    • 若子父类中定义了一模一样的成员变量,都存在于子类对象中,直接调用的是子类的变量。要在子类中直接访问同名的父类中的变量,通过super关键字来完成
      (开发一般用不到,父类定义好属性子类可以直接使用)
    • super与this用法相似
      super : 代表队是父类的内存空间
      this : 代表的是本类的对象的引用

示例代码

	class Fu
{
	void show1()
	{
		System.out.println("Fu show1 run");
	}
}

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

class ExtendsDemo1
{
	public static void main(String[] args)
	{
		Zi z = new Zi();
		z.show1();
		z.show2();
	}
}

运行结果
在这里插入图片描述

  1. 字符类中定义了一模一样的函数,运行结果是子类函数在运行。这种情况在子父类中,是函数的另一种特性:over日的(重写、覆盖、复写)
  • 示例
	需求:原来的电话只能显示号码,新电话在显示号码基础上可以显示名称和头像
	分析:新电话继承原来的电话,新电话不需要重新定义来显功能,如果子类来显
		 功能内容不同,则需要保留来显功能,定义子类的内容即可(重写的应用)

代码

	class Phone
{
	// 打电话
	void call(){}
	// 来电显示
	void show()
	{
		System.out.println("电话号码");
	}
}

class NewPhone extends Phone
{
	void show()
	{
		super.show(); // 如果还需要父类中原有的部分功能,可以通过super调用
		System.out.println("姓名");
		System.out.println("头像");
	}
}

class OverridePhone
{
	public static void main(String[] args)
	{
		NewPhone np = new NewPhone();
		np.show();
	}
}

运行结果
在这里插入图片描述

  • 注意事项
    • 子类覆盖父类。必须要保证子类权限大于或等于父类权限(如父类中为public,则子类必须为public才能保证子类覆盖父类)
    • 静态覆盖静态。
    • 写法注意必须一模一样:函数的返回值类型、函数名、参数列表都要一样
  1. 子父类中构造函数
    • 特点:先执行了父类的构造函数,再执行子类中的构造函数。因为子类中的所有构造函数中的第一行都有一句隐式语句 super(); //默认调用的是父类中的空参数的构造函数
    • 子类构造函数中为什么有一句隐式的super()?
      因为子类会继承父类中的内容,所以子类在初始化时,必须先到父类中去执行父 类的初始化动作,才可以更方便的使用父类中的内容
    • 当父类中没有空参数构造函数时,子类的构造函数必须同构显示的super语句指定要访问的父类中的构造函数

示例代码

	class Fu
{
	Fu()
	{	// 显示初始化
		System.out.println("fu constructor run A");
	}
	Fu(int x)
	{
		System.out.println("run constructor run B..."+x);
	}
}

class Zi extends Fu
{
	Zi()
	{
		System.out.println("zi constructor run C...");
	}
	Zi(int x)
	{
		System.out.println("zi constructor run D..."+x);
	}
}

class ExtendsDemo
{
	public static void main(String[] args)
	{
		new Zi();
		new Zi(6);
	}
}

运行结果
在这里插入图片描述

  • 补充

    • 如果子类的构造函数第一行写了this调用了本类其他构造函数,那么super调用父类的语句没有了,因为this()或者super()只能定义在构造函数的第一行,因为初始化动作要先执行
    • 任何构造函数默认第一行都有隐式的super()。父类构造函数中也有super(),父类的super()调用的是所有对象的父类 : Object
    • 总结:类中的构造函数默认第一行都有隐式的super()语句,访问父类的构造函数。所以父类的构造函数既可以给自己的对象初始化,也可以给自己的子类对象初始化。如果默认的隐式super的语句没有对应的构造函数,必须在构造函数中通过this或super的形式明确调用的构造函数
    • this和super语句不可以在同一个构造函数中出现,因为它们都必须定义在第一行(因为初始化动作要先执行)
  • 子类实例化过程的应用(也是super调用的应用)
    只要使用父类的指定初始化动作,就在子类中通过super(参数列表)格式进行调用

	class Person
{
	private String name;
	private int age;
	public Person(String name, int age)
	{
		this.name = name;
		this.age = age;
	}
	public void setName(String name)
	{
		this.name=name;
	}
	public String getName()
	{
		return name;
	}
	public void setAge(int age)
	{
		this.age = age;
	}
	public int getAge()
	{
		return age;
	}
	
}

class Student extends Person
{
	public Student(String name, int age)
	{
		super(name, age);
	}
	public void study()
	{
		System.out.println("study...");
	}
}
class Worker extends Person
{
	public Worker(String name, int age)
	{
		// 调用父类。使用父类的初始化动作
		super(name, age);
	}
}

class SuperApp
{
	public static void main(String[] args)
	{
		new Student("张三", 18);
	}
}
  1. final关键字
  • 是一个修饰符,可以修饰类、方法、变量(成员变量、局部变量、静态变量)
  • 特点:
    • final修饰的类是一个最终类,不能再派生类
    • final修饰的方法是最终方法,不可以重写(如果类中出现部分可以重写,部分不可以重写,可以通过让指定的方法最终化(final)即可)
    • final修饰的变量是一个常量,只能被赋值一次
  • 什么时候定义final常量?
    当程序中一个数据使用时是固定不变的,这时为了增加阅读性,可以给该数据起个名字,这就是变量。为了保证这个变量的值不被修改,加上final修饰,这就是一个阅读性很强的常量。书写规范:被final修饰的常量名所有的字母都是大写的,如果由多个单词组成,单词之间通过 _ 连接
	/*final*/ class Fu
{
	/*final*/ void show()
	{
		
	}
}

class Zi extends Fu
{
	// 重写
	void show()
	{
		final int count=21;
		System.out.println("xxx。。。"+count);
	}
}
  1. 抽象类
  • 在描述事物时,没有足够的信息描述一个事物,这时该事物就是抽象事物(如描述犬科时,发现了有些功能不具体,这些不具体的功能,需要在类中标识出来,通过Java中的关键字abstract(抽象))
  • 特点
    • 抽象类和抽象方法都需要被abstract修饰,抽象方法一定要定义在抽象类中
    • 抽象类不可以创建实例,原因:调用抽象方法没有意义
    • 只有覆盖了抽象类中所有的抽象方法后,其子类才可以实例化,否则该子类还是一个抽象类
  • 抽象函数,需要abstract修饰,并以分号 ; 结束
	abstract class Canine
	{
		abstract void roar(); // 抽象函数。需要abstract修饰,并以分号结束
	}
  • 补充
    • 抽象类一定是父类,因为不断抽取而来
    • 抽象类有构造函数,虽然不能给自己的对象初始化,但可以给子类对象初始化
    • 抽象类和一般类的异同点
      • 相同:
        1. 它们都是用来描述事物的
        2. 它们之中都可以定义属性和行为
      • 不同:
        1. 一般类可以具体的描述事务,抽象类描述事物的信息不具体
        2. 抽象类中可以多定义一个成员:抽象函数
        3. 一般类能创建对象,抽象类不能创建对象
    • 抽象类中可以不定义抽象方法,意义是不让该类创建对象
    • abstract不可以与final、private、static共存

示例

	/*
	需求:公司中程序员有姓名、工号、薪水、工作内容,项目经理在此基础上多出一项奖金
	对需求进行数据建模
*/

abstract class Employee
{
	private String name;
	private String id;
	private double pay;
	// 构造一个员工对象,一初始化就具备三个属性
	public Employee(String name, String id, double pay)
	{
		this.name=name;
		this.id=id;
		this.pay=pay;
	}
	
	// 工作行为
	public abstract void work();
	
}

// 具体的子类:程序员
class Programmer extends Employee
{
	Programmer(String name, String id, double pay)
	{
		super(name, id, pay);
	}
	public void work()
	{
		System.out.println("code...");
	}
}
// 具体的子类:经理

class Manage extends Employee
{	
	private double bonus;
	Manage(String name, String id, double pay, double bonus)
	{
		super(name, id, pay);
		this.bonus=bonus;
	}
	public void work()
	{
		System.out.println("manage...");
	}
}
  1. 接口(interface)
  • 当一个抽象类中的方法全是抽象的,可以通过接口来表示,通过interface定义
  • 接口中的成员已被限定为固定的几种,如
    • 定义变量:变量必须有固定的修饰符修饰,public static final,所以接口中的变量也称为常量
    • 定义方法:public abstract ,接口中的成员都是公共的
	interface Demo
	{
		// 定义变量
		public static final int NUM=3;
		// 定义方法
		public abstract void show1();
		public abstract void show2();
	}
  • 特点:
    • 接口不可以创建对象
    • 子类必须覆盖掉接口中所有的抽象方法后,子类才可以实例化。否则子类是一个抽象类
	// 定义子类去覆盖接口中的方法。子类必须和接口产生关系,
	//类与类的关系是继承,类与接口之间的关系是<实现>。通过关键字implements
	class DemoImpl implements Demo //子类实现接口
	{
		// 重写接口中的方法
		public void show1(){}
		public void show2(){}
	}
  • 最重要体现:解决多继承的弊端(当多个父类中有相同功能时,子类调用会产生不确定性,核心原因在于多继承父类中功能有主体,而导致调用运行时,不确定运行哪个内容),将多继承这种机制在Java中通过多实现完成(因为接口中的功能都没有方法体,由子类来明确)
	interface A
	{
		void show();
	}
	interface B
	{
		void show();
	}
	class C implements A,B //多实现。同时实现多个接口
	{
		public void show();
	}

...
C c = new C();
c.show();
  • 基于接口的扩展
    接口的出现避免了单继承的局限性
    父类中定义的事物的基本功能
    接口中定义的事物的拓展功能
	class Fu
	{
		public void show(){}
	}
	// 子类通过继承父类扩展功能,通过继承扩展的功能是子类应该具备的基础功能
	// 如果子类想要继续扩展其它类中的功能,通过接口来完成
	interface Inter
	{
		public void show1();
	}
	class Zi extends Fu implements Inter
	{
		public void show1(){}
	}
  • 扩展
    • 类与类之间是继承关系(is a),类与接口之间是实现关系(like a), 接口与接口之间是继承关系,而且可以多继承
	interface InterA
	{
		void show1();
	}
	interface InterAA
	{
		void show11();
	}
	interface InterB extends InterA, InterAA //接口的多继承
	{
		void show2();
	}
	class Test implements InterB
	{
		public void show1(){}
		public void show2(){}
		public void show11(){}
	}
  1. 接口思想:
  • 接口的出现对功能实现了扩展
  • 接口的出现定义了规则
  • 接口的出现降低了耦合性(解耦)
  • 接口的出现,完成了解耦,说明有两方,一方在使用此规则,一方在实现此规则。
  1. 抽象类和接口简单区别
  • 抽象类是描述事物的基本功能,可以定义非抽象的方法。接口中定义只能是抽象方法,负责功能的扩展。
  • 类与类之间是继承关系(is a),类与接口之间是实现(like a)关系
  1. 多态
  • 前提
    • 必须有关系 :继承或实现
    • 通常有重写操作
  • 体现:
    父类的引用或者接口的引用指向了自己的子类对象
    Dog d = new Dog();// Dog对象的类型是Dog类型
    Animal a = new Dog(); // Dog对象的类型右边是Dog类型,左边是Animal类型
  • 好处
    • 提高了程序的扩展性
  • 局限
    • 通过父类引用操作子类对象时,只能使用父类中已有的方法,不能操作子类特有的方法
  1.  	Animal a =  new Dog(); //Animal是父类型,new Dog()是子对象
    
    但是父类型应用指向子对象时,这就是让子类对象进行了类型的提升(向上转型)
  • 向上转型好处:提高了扩展性,隐藏了子类型
  • 向上转型弊端:不能使用子类型的特有方法
    如果要想使用子类的特有方法,只有子类型可以用。可以向下转型,强制转换。
	Animal a = new Dog();

a.eat();
Dog d = (Dog)a;//将a转型为Dog类型,向下转型
d.lookhome();

  • 无论是向上还是向下转型,最终都是子类对象做着类型的变化

  • 向下转型的注意事项:
    Animal a = new Dog();
    //Cat c = (Cat)a; 向下转型因为不明确具体子类对象类型,所以容易引发ClassCastException异常。所以为了避免这个问题,需要在向下转型前,使用instanceof关键字做类型的判断。

    if(a instanceof Cat) //a指向的对象的类型是Cat类型
    {
    	// 将a转型Cat类型
    	Cat c = (Cat)a;
    	c.catchMouse();
    }
    else if (a instanceof Dog)
    {
    	Dog d = (Dog)a;
    	d.lookHome();
    }
    
    
  • 转型总结

    1. 什么时候使用向上转型?
      • 提高程序的扩展性,不管行子类型(子类型被隐藏)
      • 判断方法:如果不需要用子类的特定方法,则使用向上转型,反之使用向下转型
    2. 什么时候需要向下转型?
      • 需要使用子类型的特有方法时
      • 但是一定要使用instanceof进行类型的判断,避免发生 ClassCastException
  • 多态中成员变量:

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

    • 编译时期:参考左边,如果没有,编译失败
    • 运行时期:参考右边对象所属的类
    • 简记为:编译看左边,运行看右边
    • 成员函数是动态绑定到对象上
  • 多态中的静态函数:

    • 编译和运行都看左边
    • 静态函数是静态的绑定到类上
    • 真正开发中静态方法是不会被多态调用的,因为静态方法不所属于对象,而是所属于类
  • 结论:对于成员变量和静态函数,编译和运行都看左边;对于成员函数,编译看左边,运行看右边

  1. Object
  • Object类是所有类的根类,定义了所有对象都具备的功能
  • Object equals方法 , [! 以后判断对象是否相同,就需要覆盖equals方法 ]
	class Person extends Object
{
	private int age;
	Person(int age)
	{
		this.age=age;
	}
	// 判断是否是同龄人,这个方法也是在比较两个person对象是否相等
	// 因为Person类继承Object,所以它本身就具备着equals方法
	// 但是equals方法判断的是地址,不是我们所需要的内容,需要对equals进行方法重写
	public boolean equals(Object obj) 
	{
		//建立Person自己的判断相同的依据,判断年龄是否相同
		// return this.age==obj.age; // 所属类型object,object没有定义age,所以编译失败
		
		//如果调用该方法的对象和传递进来的对象是同一个,就不需要转型,直接返回true,提高效率
		if(this==obj)
			return true;
		
		// age是Person类型的属性,既然要用到子类型的内容,需要向下转型
		if(!(obj instanceof Person))
			throw new ClassCastException("类型错误!请改正");
		Person p = (Person)obj;
		
		return this.age==p.age;
		
	}
	
}

class ObjectDemo
{
	public static void main(String[] args)
	{
		Person p1 = new Person(12);
		Person p2 = new Person(15);
		System.out.println(p1.equals(p2));
	}
	
}
  • Object toString方法,重写toString方法,建立新的字符串表现形式
    public String toString()
    {
    	return "xxx"
    }
    
    
  1. 内部类
  • 当A类中的内容要被B类直接访问,而且A类还需要创建B的对象,访问B的内容时,可以将B类定义到A类的内部,此时将B称之为内部类(内置类、嵌套类)
  • 当描述事物时,事物的内部还有事物,这个内部的事物还在访问外部事物中的内容,这时就将这个事物通过内部类来描述
  • 访问方式:内部类可以直接访问外部类中的所有成员,包含私有的;而外部类要想访问内部类中的成员,必须创建内部类的对象
    示例代码
   class Outer // 外部类
{
   private int num=6;
   
   class Inner // 内部类
   {
   	void show()
   	{
   		System.out.println("num+"+num);
   	}
   }
   
   void method()
   {
   	Inner in = new Inner();
   	in.show();
   }
   
}

class InnerClassDemo
{
   public static void main(String[] args)
   {
   	Outer out = new Outer();
   	out.method();
   }
}

运行结果
在这里插入图片描述

  • 内部类在成员位置上的被访问方式

    • 成员可以被指定的修饰符所修饰
      • public:不多见,因为更多的时候内部类已经被封装到了外部类中,不对外直接提供
      • static:非静态内部类中不允许定义静态成员,仅允许在非静态内部类中定义静态常量 static final。如果想在内部类中定义静态成员,必须内部类也要被静态修饰
  • 内部类被静态修饰后,随着外部类加载而加载,可以把一个静态内部类理解成一个外部类

  • 直接访问Outer中的Inner内部类的非静态成员:
    创建内部类的对象。内部类作为成员,应该先有外部类对象再有内部类对象

    	Outer.Inner in = new Outer().new Inner();
    	in.show();
    
  • 对静态内部类中的非静态成员进行调用:
    因内部类是静态,所以不需要创建Outer的对象,直接创建内部类对象

   Outer.Inner2 in = new Outer.Inner2();
   in.show2();
  • 访问静态内部类的静态成员:
    因静态内部类已随外部类加载,且静态成员随着类的加载而加载,就不需要对象,直接使用类名调用即可
	Outer.Inner2 .staticShow();
  • 当静态方法访问内部类时,内部类必须是静态的
  1. 内部类能直接访问外部类中成员的原因:
    因为内部类中持有了外部类的引用 <外部类.this> ;对于静态内部类不持有 <外部类.this>,而是直接使用 <外部类名.>
  2. 内部类定义在局部时,只能访问被final修饰的局部变量,因为编译产生的class直接操作被修饰的局部变量代表的数值了。不能访问非最终局部变量,因为非最终局部变量生命周期太短
    内部类定义在外部类局部位置示例:
	class Outer
	{
		int num=3;
		void method()
		{
			class Inner//局部内部类,不能被成员修饰符修饰(如public等)
			{
				void show()
				{
					System.out.println("Inner show run..."+num);
				}
			}
		}
	}
  • 看API,发现类名或者接口名称中有 . 说明是内部类或者内部接口
  • 内部类可以继承或者实现外部其他的类或者接口,好处:通过内部类的方式对类进行继承重写或者接口进行实现,通过公共的方式对其内部类对象进行访问,因为通常内部类很有可能被外部类封装其中,我们可以通过父类或者接口的方式访问到内部类对象
  • 匿名内部类:其实就是一个带有内容的子类对象
  • 格式: new 父类/接口()(子类的内容)
  • 匿名内部类就是内部类的简化形式,但有前提:内部类必须要继承父类或者实现接口
  • 匿名内部类中一般方法不要过多,阅读性会变差
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

SimpleZihao

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值