菜鸟学习笔记:Java基础篇5(抽象类与接口、回调函数、内部类)

23 篇文章 1 订阅
23 篇文章 0 订阅

菜鸟学习笔记:Java面向对象篇下

抽象类

通过前面知识的学习,抽象类这个概念应该不难理解,但比较容易和后面要说的接口混淆,而且在面试中也比较爱问这两者的区别,所以希望大家可以通过对比来学习。
抽象类可以理解为它子类一个模板,所有继承了抽象类的子类都必须遵守抽象类的规定,子类可以在这个模板上进行扩展。通过抽象类可以避免子类的随意性。
还是之前Animal的例子,我们把Animal定义成一个抽象类:

//在访问控制符后加上abstract代表为抽象类或方法
public abstract class Animal{//抽象类
	int old;   //注意Java中没有抽象属性
	String environment;
	String metabolize;

//抽象方法定义时加上abstract关键字,只能定义在抽象类和接口中(接口中方
//法默认为抽象方法)该类方法没有方法体,并且规定子类中必须实现这些方法
	abstract void reproduction();//默认为public方法
	abstract void move();
//抽象类也可以定义普通方法
	public void grow(){
		System.out.println("我在成长。。。");
	}
}

它的子类:

class Fish extends Animal{
	//必须实现abstract方法
	public void reproduction(){
		System.out.println("小鱼在繁殖。。。");
	}
	public void move(){
		System.out.println("小鱼游啊游。。。");
	}
}
//鸟类继承动物类
class Bird extends Animal{
	//必须实现abstract方法
	public void reproduction(){
		System.out.println("小鸟在繁殖。。。");
	}
	public void move(){
		System.out.println("小鱼飞啊飞。。。");
	}
	//也可以重写普通方法
	public void grow(){
		System.out.println("小鸟在成长。。。");
	}
}

对于抽象类有几个注意点:

  • 用abstract修饰的方法只能在抽象类中(接口中的方法也是抽象方法,但不需要用abstract修饰)
  • 抽象类只能被继承,无法实例化,但可以实现多态(抽象类引用指向子类对象)
  • 抽象类可以有构造方法,但不能用来new实例,只能用来被子类调用(super())。

接口

自然界中有些事务的关系很复杂,比如一根火腿肠,它属于肉类,但也属于零食类,还属于商品类。但Java之中的继承规定是单继承,一个类只能有一个父类,所以依靠继承关系无法全面的反应出自然界里所有的关系,这时候就需要引入一个全新的概念:接口
接口与抽象类有一定的相似性,但也有区别,首先在接口中不允许定义普通方法,这让接口变得比抽象类更加严格。其次一个类可以实现多个接口,并且类中要实现它所有接口中的方法,这一点很好的实现了一个对象同时属于不同类的情况。举例说明:

//在访问控制符后加上interface代表为接口
public interface Animal{//Animal接口
	//接口中的变量即使不做声明,注意接口中的属性默认为
	//static final类型,不可改变,并且必须赋予初值
	int old=6;   
	String environment = "water";
	
	//即使不做声明,接口中的方法也默认为public abstract方法。
	void reproduction();
	void move();
}

public interface MarineOrganism{//海洋生物接口
	void BreatheWithGills();
}

这样鱼类就可以实现这两个接口:

class Fish implements Animal,MarineOrganism{
	public int height = 5;
	//必须实现接口中的所有方法
	public void reproduction(){
		System.out.println("小鱼在繁殖。。。");
	}
	public void move(){
		System.out.println("小鱼游啊游。。。");
	}
	public void BreatheWithGills(){
		System.out.println("小鱼用鳃呼吸。。。");
	}
}

在main函数中:

public static void main(String[] args) {
	Animal oFish = new Fish();  //接口和抽象类一样也可以作为引用变量类型,指向实现类类型的对象
	System.out.println(oFish.old); //实现类可以获得接口中的属性,但不可以修改
	
	//这里补充一点,因为静态属性和方法随类的加载而定义,先于对象,
	//所以其不仅可以通过类名进行调用,也可以通过类的实例来调用,调
	//用效果和类名调用一致,但还是建议用类名调用静态方法
	
	((Fish)oFish).height = 4;
	//oFish的引用类型为Animal,Animal接口没有height这个属性,
	//所以需要通过强制类型转换来解决这个问题,下面方法同理
	((Fish)oFish).BreatheWithGills(); //调用方法会打印小鱼用鳃呼吸。。。
}

对于抽象类和接口的区别一直是面试的考点,相信大家根据上面的讲解应该可以自己总结出来,我找了一篇专门做对比的博客分享给大家。链接

回调函数

首先大家应该都知道我们可以把一块实现同一功能的代码块封装成一个方法,在使用时可以直接调用。但在程序中有时候会出现一个问题,某一段开始和结束的逻辑相同,但中间的代码存在差异。假设把每个人使用电脑的过程写成一段代码:

public void UseComputer(){
	System.out.println("做到电脑前");
	System.out.println("按开机键");
	//中键过程每个人都不相同
	System.out.println("按关机键");
	System.out.println("离开电脑前");
}

这段代码中前后过程一致,但中间操作不同人大不相同,而前后的代码无法进行封装,所以我们采用回调函数来解决这个问题。
首先新建一个接口:

public interface Operation {
   public void Do();
}

然后我们新建一个程序员类来继承操作接口,重写Do方法:

public class Programmer implements Operation{
   @Override
   public void Do() {
   	System.out.println("写代码,写代码。。。");
   }
}

改写UseComputer方法为:

public void UseComputer(Operation o){
	System.out.println("做到电脑前");
	System.out.println("按开机键");
	o.Do();
	System.out.println("按关机键");
	System.out.println("离开电脑前");
}

这样我们针对不同身份的人使用电脑,就可以调用不同的函数实现,这就是回调函数。我们在main函数中做测试:

	public static void main(String[] args) {
   		Programmer oProgrammer = new Programmer();
   		new test().UseComputer(oProgrammer);
   		//打印
   		//做到电脑前
   		//按开机键
   		//写代码,写代码。。。
   		//按关机键
   		//离开电脑前
   }

回调函数主要运用了23种设计模式中所讲的模板方法模式(感兴趣的同学可以去了解了解,这里不做太多介绍),回调函数的运用非常广泛,比如我们在点击某个按钮时按钮接收响应的方式都是一样的,唯一不同的就是点击时要做的操作,所以我们就可以将这些操作用回调函数传入其中,大大简化开发复杂度。
个人感觉回调函数这个概念对老手很简单,但新手不太好理解,大家如果看完没有理解可以结合这个例子再看一些介绍概念的博客。我在这里推荐一篇 ,相信大家多看多写很快就能掌握。

内部类

简单的说就是在另一个类里面定义的类,这样使得这个类只能是它的外部类访问,对其他类不可见。而且这个类可以访问外部类的私有属性更加灵活方便。一般情况下内部类只有为外部类提供服务的情况下优先使用。

成员内部类

成员内部类分为静态内部类和非静态内部类,和静态和非静态属性和方法的区别相似,先举一个非静态内部类的例子:

public class Human{
  int age;
  String name;
  
  public Human(int age, String name) {
  	super();
  	this.age = age;
  	this.name = name;
  }
  
  public void Grow() {
  	System.out.println("我在成长。。。");
  }

  class Eyes{
  	String color = "黄";
  	void see(){
  		System.out.println(color + "眼睛的" + name + "看到你们了。。。");
  	}
  }
}

这样在调用时:

   public static void main(String[] args) {
   	Human oHuman = new Human(30,"老王");
   	Eyes e = oHuman.new Eyes();//实例化内部类
   	e.see();//会打印黄眼睛的老王看到你们了。。。表名内部类可以访问外部类属性
   }

注意非静态内部类不能有静态属性、静态方法、静态代码块。
而静态内部类和静态成员方法对应,并不依赖于外部类对象,所以它不能用外部类对象的属性和方法,但可以访问外部类的静态成员。稍微修改下上一个例子:

public class Human{
  int age;
  String name;
  static final int numberOfeyes = 2;
  
  public Human(int age, String name) {
  	super();
  	this.age = age;
  	this.name = name;
  }
  
  public void Grow() {
  	System.out.println("我在成长。。。");
  }

  static class Eyes{
  	String color = "黄";
  	void see(){
  		System.out.println("人有" + numberOfeyes + "只眼睛");
  	}
  }
}

调用时:

	public static void main(String[] args) {
		//Human oHuman = new Human(30,"老王");
		Human.Eyes e = new Human.Eyes();
		e.see();
	}

匿名内部类

适合那种只需要使用一次的类,比如键盘监听操作,运用静态内部类我们可以更好的使用回调函数操作,在使用回调函数时我们可以不定义程序员Programmer类,而直接用静态内部类实现:

	public static void main(String[] args) {
   		new test().UseComputer(new Operation() {
   			@Override
   			public void Do() {
   				System.out.println("写代码,写代码。。。");
   			}
   		});
   		//打印
   		//做到电脑前
   		//按开机键
   		//写代码,写代码。。。
   		//按关机键
   		//离开电脑前
   }

匿名内部类通常用来简化代码编写,但使用匿名内部类还有个前提条件:必须继承一个父类或实现一个接口,但最多只能继承一个父类,或实现一个接口。
至此Java面向对象方面的知识就全部讲完了,希望大家学到这对Java的编程思想有个清晰的认识,在之后几篇中会介绍几个Java的常用类,帮助大家更更好的运用Java来编程。
上一篇:菜鸟学习笔记:Java基础篇4(面向对象三大特征).
下一篇:菜鸟学习笔记:Java基础篇6(数组、字符串).

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值