从零开始学java(22)--- Java中的类

Day12

1.类的初始化顺序

实例化类:
父类静态属性或静态代码块↓
子类的静态属性或静态代码块↓
父类的代码块↓
父类的构造方法↓
子类的代码块↓
子类的构造方法↓

注:静态代码块或静态属性只会初始化一次,当你第二次或多次实例化就不会再初始化静态代码块或属性。

静态方式(没有实例化):
父类静态属性或静态代码块↓
子类的静态属性或静态代码块

2.抽象类

语法:
使用abstract修饰
特点:
1.抽象类不可实例化。

2.在抽象类中可以有抽象方法,使用abstract修饰,没有方法体,以;结尾。

3.反过来,抽象方法必须存在于抽象类中
如果具体类继承了抽象类,必须实现(重写)抽象类中所有的抽象方法
如果抽象类继承了抽象类,可以不实现抽象方法

4.抽象类可以有构造方法吗,子类的构造方法会调用其构造方法。

抽象类与抽象方法的使用(多态的场景):
父类不知道子类的实现逻辑,
父类需要子类的处理逻辑,
子类中各自实现自己的逻辑。

3.接口

概念:Java接口是一系列方法的声明,是一些方法特征的集合,一个接口只有方法的特征没有方法的实现,因此这些方法可以在不同的地方被不同的类实现,而这些实现可以具有不同的行为(功能)。

我们可以这样理解:
有时必须从几个类中派生出一个子类,继承它们所有的属性和方法。但是,Java不支持多重继承。有了接口,就可以得到多重继承的效果。
也可以说:
接口可以理解为一种特殊的类,里面通常是由全局常量和公共的抽象方法所组成。接口是解决Java无法使用多继承的一种手段,但是接口在实际中更多的作用是制定标准的。

特点:

1.只有public static final修饰的常量,抽象方法组成
声明时我们可以省去public static final,和public abstract,这些属性是默认的:

public interface Flyable {
	int SPEED=100;//默认public static final
	void fly();//默认public abstract
}

注:final修饰的常量命名:所有的字母大写,多个单词以_分隔(默认规范);
2.使用关键字interface声明接口

3.使用关键字implements实现接口,子类是具体类,要使用接口中的所有抽象方法。
类可以同时实现多个接口,也可以同时继承父类

在Java8中,接口有一些新的特点:

1.JDK1.8以后,接口的定义被打破了,要定义一个接口,接口中不仅可以定义全局常量和抽象方法。

(JDK1.8以前,要定义一个接口,接口中只能定义全局常量和抽象方法)

接口中还可以使用default关键字定义普通方法,这个方法有方法体,而且子类默认继承,不需要必须复写此方法,通过类的实例化对象来调用此方法。

接口中还可以使用static关键字定义静态/类方法,这个方法有方法体,而且子类继承不了接口中的static方法,因此更谈不上子类复写此方法,只能通过接口的名字来调用此方法。

注:
(1).如果一个类实现了多个接口,恰巧多个接口中有相同的default修饰的方法,那么这个类必要要手动实现该默认方法,否则会报错。因为类的实例化对象不知道该调用哪个父类的efault修饰的方法,所以必须子类重写多个接口中有相同的default修饰的方法。
(2).实现接口的类或者子接口不会继承接口中的静态方法,这个静态方法的只能由接口名调用,不能由子类对象调用。

2.函数式接口(Functional Interface)是只有唯一一个抽象方法的接口。

(1).函数式接口中的抽象函数就是为了支持 lambda表达式;
(2).函数式接口可以被隐式转换为lambda表达式;
(3).为确保函数式接口符合语法,可以添加@FunctionalInterface注解;

一些拓展

1.在表达某种特点或能力,命名以able结尾

2.接口可以看成一种协议,在分层时用于确定下一层具有哪些功能。下级结构中的类要履行接口协议,来执行定义在接口中的方法。

3.接口的使用,可以达到解耦的目的。
接口就是标准,遵守标准的实现(implements)就能够在标准被调用的地方使用。
我们可以这样理解,usb口的标准假如生产鼠标的厂商A用一套,厂商B又用一套,生产键盘的厂商又自成一套标准,那么这些厂商肯定还得生产自己的主机。现在我们就可以理解为鼠标A厂商的主机和他的鼠标是严重耦合的,A的鼠标和B的主机肯定不能搭配使用。如果有一套标准也就是接口,去规范这些,那这些东西就随便组合了,也就是所说的解耦 。
苹果就是严重耦合的。。。

4.java的父类声明,子类实例化

这在我昨天也举了这样的例子
父类 实例=new 子类();
父类声明,子类实例化,既可以使用子类强大的功能,又可以抽取父类的共性。

特点:
(1).父类类型的引用可以调用父类中定义的所有属性和方法;

(2).父类中方法只有在是父类中定义而在子类中没有重写的情况下,才可以被父类类型的引用调用;

(3).对于父类中定义的非静态方法,如果子类中重写了该方法(后期/动态绑定:重写后的方法),那么父类类型的引用将会调用子类中的这个方法,静态方法还是调用父类的(前期/静态绑定:静态方法);

(4).父类和子类声明同名的成员变量,获得的是父类的成员变量(前期/静态绑定:成员变量);

(5).无法调用子类特有的方法,即非重写父类方法所得到的方法。

1.向上转型:子类的实例可以自动转型为父类的类型,在实例中访问的方法不能包含子类中所有声明的方法。

  Animal dog1 = new Dog();

多态的典型体现:方法的参数是父类,所有的子类实例可以传进去。

2.向下转型:父类型的实例使用()这种强制转换的语法转型为子类的类型。
可能存在转型失败的场景,在转换之前,使用instanceof判断,成功之后再进行转换

Dog dog2 = (Dog)dog1;//右边就是将Animal类型的dog1转成Dog类型

5.绑定:变量最终指向谁

概念:绑定指的是一个方法的调用与方法所在的类(方法主体)关联起来。对java来说,绑定分为静态绑定和动态绑定;或者叫做前期绑定和后期绑定.

前期(静态)绑定:

static,final,private,构造方法,成员变量(包括静态及非静态)是前期绑定。

在程序执行前方法已经被绑定(也就是说在编译过程中就已经知道这个方法到底是哪个类中的方法),此时由编译器或其它连接程序实现。
针对java简单的可以理解为程序编译期的绑定;

后期(动态)绑定:

在运行时根据具体对象的类型进行绑定。除了前期绑定都是后期绑定。

若一种语言实现了后期绑定,同时必须提供一些机制,可在运行期间判断对象的类型,并分别调用适当的方法。也就是说,编译器此时依然不知道对象的类型,但方法调用机制能自己去调查,找到正确的方法主体。不同的语言对后期绑定的实现方法是有所区别的。但我们至少可以这样认为:它们都要在对象中安插某些特殊类型的信息。

动态绑定的过程:
虚拟机提取对象的实际类型的方法表;
虚拟机搜索方法签名;
调用方法。

关于final,static,private和构造方法是前期绑定的理解:

1.对于private的方法,首先一点它不能被继承,既然不能被继承那么就没办法通过它子类的对象来调用,而只能通过这个类自身的对象来调用。因此就可以说private方法和定义这个方法的类绑定在了一起。

2.final方法虽然可以被继承,但不能被重写(覆盖),虽然子类对象可以调用,但是调用的都是父类中所定义的那个final方法,(由此我们可以知道将方法声明为final类型,一是为了防止方法被覆盖,二是为了有效地关闭java中的动态绑定)。

3.构造方法也是不能被继承的(网上也有说子类无条件地继承父类的无参数构造函数作为自己的构造函数,不过这个说法是不太恰当的,因为我们知道子类是通过super()来调用父类的无参构造方法,来完成对父类的初始化, 而我们使用从父类继承过来的方法是不用这样做的,因此不应该说子类继承了父类的构造方法),因此编译时也可以知道这个构造方法到底是属于哪个类。

4.对于static方法,可以被子类继承,但是不能被子类重写(覆盖),但是可以被子类隐藏。(这里意思是说如果父类里有一个static方法,它的子类里如果没有对应的方法,那么当子类对象调用这个方法时就会使用父类中的方法。而如果子类中定义了相同的方法,则会调用子类的中定义的方法。唯一的不同就是,当子类对象上转型为父类对象时,不论子类中有没有定义这个静态方法,该对象都会使用父类中的静态方法。因此这里说静态方法可以被隐藏而不能被覆盖。这与子类隐藏父类中的成员变量是一样的。隐藏和覆盖的区别在于,子类对象转换成父类对象后,能够访问父类被隐藏的变量和方法,而不能访问父类被覆盖的方法)

由上面我们可以得出结论,如果一个方法不可被继承或者继承后不可被覆盖,那么这个方法就采用的绑定。

以上内容部分参考自https://www.cnblogs.com/jstarseven/articles/4631586.html

6,类之间的关系

在设计模式中类与类之间的关系主要有6种:依赖、关联、聚合、组合、继承(泛化),实现,它们之间的耦合度依次增加。

1.依赖(Dependency)关系:对象之间最弱的一种关联方式,是临时性的关联。代码中一般指由局部变量、函数参数、返回值建立的对于其他对象的调用关系。
其中一个类的变化将影响另外一个类,是一种“use a”关系。如果A依赖于B,则B表现为A的局部变量,方法参数,静态方法调用等

2.关联(Association)关系:是类与类之间最常用的一种关系,它是一种结构化关系,用于表示一类对象与另一类对象之间有联系,通常是属性的存在,如汽车和轮胎、师傅和徒弟、班级和学生等等。

单向或双向(通常我们需要避免使用双向关联关系),默认是双向,单向是一种"has a"关系,如果A单向关联B,则可以说A has a B,通常表现为全局变量。

3.聚合(Aggregation):表示“has-a”的关系,是一种不稳定的包含关系。较强于一般关联,有整体与局部的关系,并且没有了整体,局部也可单独存在。比如说员工和老板,员工可以独立存在,就算是公司倒闭了,员工还可以换工作。
也可以说是单向关联关系的一种,与关联关系之间的区别是语义上的,关联的两个对象通常是平等的,聚合则一般不平等,有一种整体和局部的感觉、

4.组合 (Composition) : 表示“contains-a”的关系,是一种强烈的包含关系。组合类负责被组合类的生命周期。是一种更强的聚合关系。部分不能脱离整体存在。如人和大脑的关系,没有了人,大脑也不能存在了。
单向,是一种强依赖的特殊聚合关系。

5,继承关系(Inheritance):“is-a”的关系,是一种用于表示特殊与一般关系的。
类继承抽象类,类继承父类,对应的是extend关键字,属于这种关系。

6.实现(Realization): “like a”的关系。
类实现接口,对应implement关键字,属于这种关系。

可以阅读Java编程思想第四版,来更好的体会这些关系。

7,内部类(了解一下)

在GUI开发中,事件处理中会使用到
概念:
在类或接口中声明的类。将一个类A定义在另一个类B里面,里面的那个类A就称为内部类,B则称为外部类。我们通过例子来了解。

public class OutClass {
	   class InnerClass1{//成员内部类
	  public void test1(){
		  System.out.println("1");
	  }
	   }
	  static class InnerClass2{//静态内部类
	  public void test2(){
		  System.out.println("2");
	  }
	  }
	  
	  void test(Flyable a) {
		 a.fly();
	  }
}
public class OutTest {
	public static void main(String[] args) {
		//注意调用语法
		OutClass.InnerClass1 class1 = new OutClass().new InnerClass1();
		//外部类名.内部类名 对象名 = new 外部类名().new 内部类名();
		class1.test1();

		OutClass.InnerClass2 class2 = new OutClass.InnerClass2();
		//外部类名.内部类名 对象名 = new 外部类名.静态内部类名();
		class2.test2();

		OutClass test1 = new OutClass();
		test1.test(new Flyable() {//匿名内部类

			@Override
			public void fly() {
				System.out.println("fly");

			}
		});

	}
}

最后输出:在这里插入图片描述

匿名内部类:

一个继承了类的子类的匿名对象 或者一个实现了接口的实现类的匿名对象
开发中,最常用到的内部类就是匿名内部类了。以接口举例,当你使用一个接口时,似乎得做如下几步操作。
1、定义子类
2、重写接口中的方法
3、创建子类对象
4、调用重写后的方法
我们的目的,最终只是为了调用方法,那么能不能简化一下,把以上四步合成一步呢?匿名内部类就是做这样的快捷方式。

语法:

new 父类名或者接口名(){
    // 方法重写
    @Override 
    public void method() {
        // 执行语句
    }
};

条件:
匿名内部类必须继承一个父类或者实现一个父接口。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值