您的位置:首页 > 编程语言 > Java开发

7.偏头痛杨的Java入门教学系列之进阶面向对象篇

2017-06-13 14:04 309 查看
复习
1.Java有多少种数据类型,数据类型的分类?数据类型的大小排序?默认类型?默认值?
2.Java的工作机制?
3.Java中有多少种初始化数组的方式?
4.什么是变量,如何定义变量?Java中有多少种变量?分别是什么以及区别?
5.循环体内return、continue、break的区别?
6.什么时候会出数组下标越界?
7.for循环的三个表达式的作用?
8.String s[][] = new String[2][2];与String s[][] = new String[2][];的含义
9.方法有几种?分别是什么?区别是什么?
10.JDK&JRE&JVM分别是什么以及他们的关系?

前文链接:
1.偏头痛杨的Java入门教学系列之认识Java篇
http://blog.csdn.net/piantoutongyang/article/details/70138697
2.偏头痛杨的Java入门教学系列之变量&数据类型篇
http://blog.csdn.net/piantoutongyang/article/details/70193622
3.偏头痛杨的Java入门教学系列之表达式&运算符&关键字&标识符&表达式篇
http://blog.csdn.net/piantoutongyang/article/details/71027446
4.偏头痛杨的Java入门教学系列之初级面向对象篇
http://blog.csdn.net/piantoutongyang/article/details/78135129
5.偏头痛杨的Java入门教学系列之流程控制语句篇
http://blog.csdn.net/piantoutongyang/article/details/71698589
6.偏头痛杨的零基础学Java系列之数组篇
http://blog.csdn.net/piantoutongyang/article/details/72510787

前戏
记得小时候语文老师教过我们记叙文六要素:时间、地点、人物,事件的起因、经过、结果,对吧?
那么Java的三要素是:继承 、封装 、多态。
我们围绕着Java三要素,来展开一些其他的重要概念,例如接口,抽象类,方法的重写&重载等等。
本节课的概念与类&对象的概念为承上启下的关系,是非常重要的概念。

继承(Inheritance,也称为泛化)
继承是面向对象编程实现软件复用的重要手段,当子类继承父类后,子类作为一种特殊的父类,
将直接自动获得父类的属性和方法(拿来就用,不用自己写)。
同时子类也可以增加自己的属性和方法以及重新定义父类的属性、重写父类的方法可以获得与父类不同的功能。

在类层次结构中,对父类的改动自动反映在它所有的子类,子类的子类中,
不需要修改或重新编译任何低层次的类,他们通过继承而接收新信息,
仅仅需要在层次结构中定义行为和属性一次,以后将自动由所有子类所继承。

(父类也叫超类、基类,子类也叫派生类)
(爸爸抽中华,儿子天生就抽中华。爸爸改成抽雪茄,儿子自动抽雪茄。但爸爸不会喝酒,儿子可以会喝酒。)

当两个类具有相同的特征(属性)和行为(方法)时,可以将相同的部分抽取出来放到一个类中作为父类,其它两个类继承这个父类。

在定义一个类时,可以在一个已经存在的类的基础之上来进行,把这个已经存在的类所定义的内容作为自己的内容,
并加入自己若干新的内容。继承有利于软件的复用,避免重复代码的出现,可以提高开发效率。

生活中的例子:



还能举出其他的例子吗?

 继承是类之间的一种关系,一般类与特殊类之间的关系,继承关系的语义:“is a”。
父类更通用,子类更具体。

老虎类(子类)是一种食肉动物类(父类)
tiger is a carnivore.

食肉动物类(子类) 是一种动物类(父类)
carnivore is an animal.

虽然食草动物和食肉动物都是属于动物,但是两者的属性和行为上有差别,
所以子类会具有父类的一般特性也会具有自身的特性。

语法:
[修饰符] class 子类名 extends 父类名{
  ...
}

注意:
~子类无法继承父类的构造方法。
~Java只支持类的单继承,每个类只能有一个父类,儿子只能有一个爸爸,不允许有多个爸爸,爸爸只能有一个爷爷,
一个爸爸可以有多个儿子。
~子类拥有父类非private的属性与方法。(注意private的属性和方法无法被继承)
~子类可以拥有自己的属性和方法,子类可以对父类进行扩展,子类可以用自己的方式实现父类的方法。
~继承提高了类之间的耦合性(继承的缺点,耦合度高就会造成代码之间的联系)。
~所有的类都是继承于java.lang.Object,不用显式的写extends。

class Animal{
  String color;
  int age;
  public void eat(){
    System.out.println("觅食");
  }
}

class Tiger

extends
Animal{
  public void attack(){
    System.out.println("攻击");
  }
}

class Main{
  public static void main(String args[]){
    Tiger t1 = new Tiger();
    t1.attack();
   

t1.eat();//儿子自动继承爸爸的方法
   

t1.age = 5;//儿子自动继承爸爸的属性
  }
}

实例化子类对象时总会在执行子类构造方法之前去调用父类构造方法,
(先实例化父类,再实例化子类,继承的类很多的话会影响性能)

注意:
如果父类有无参构造,则默认先调用父类的无参构造
(子类不用显式的调用super(),系统会自动调用父类的无参构造方法。 )。
如果父类没有无参构造,则需要程序员显式的在子类构造中通过super()来调用父类构造,且必须在第一行调用。
如果父类只有有参构造,而子类没有显式的去调用则会报错。
如果父类只有有参构造,而子类显式的去调用父类的无参构造则会报错。

应用场景:
例如有两个类,这两个类中有一部分相同的方法与属性(相同部分属于重复代码)。
如果没有父类的话,如果相同部分需要修改就要改2个地方(维护性低)。
因此我们把这两个类中相同的部分抽出来,形成一个父类。
之后让这两个类继承父类,这样这两个子类就不会纯在重复代码,维护性提高,代码也简洁了。

this与super关键字
this()与super()必须定在在构造方法中,并且必须在第一行。

super()
作用:调用父类的构造方法,只能出现在子类的构造器中,且必须是第一行
~super()中的参数,决定了调用父类哪个构造方法
~如果子类构造方法中没有出现super,那么编译器会默认加上super(),即调用父类的无参构造方法,
如果父类没有无参构造,编译器提示错误。

class Father{
  public Father(){
    System.out.println("这里是爸爸哟");
  }
}

class Son extends Father{
  public Son(){
   

super();//显式调用父类构造方法
    System.out.println("这里是儿子哟");
  }
}

class Test{
  public static void main(String[] args){
    Son s = new Son();
  }
}

this()
作用:调用本类的构造方法。

public Son() {
    super("jack");
    System.out.println("儿子被构造啦");
  }

  public Son(String name) {
   

this();//显式调用无参构造方法
    System.out.println(name + "娃哈哈");
  }

注意:
~this()&super()不能出现在static的方法与static块中。
~this()调用和super()调用不会同时出现。

super.指向父类的引用,我们可以通过super关键字来实现对父类成员的访问。
this.指向本类的引用,我们可以通过this关键字来实现对本类成员的访问。

class Father{
  int age = 50;
}

class Son extends Father{
  int age = 20;
  public void say(){
   

System.out.println(super.age);//输出50
   

System.out.println(this.age);//输出20
  }
}

class Main{
  public static void main(String[] args){
    Son s = new Son();
    s.say();
  }
}

final关键字(经典初级Java面试题)

final可以修饰的元素:

类:不能被继承,类被定义成final,那么类中的所有方法全部为final。

方法:不能在子类中被覆盖(除private方法外),即不能修改。

变量:不能被重新赋值,就是之前教过大家的常量。

包(package)
包主要用来对类和接口进行分类。当开发Java程序时,可能编写成百上千的类,因此很有必要对类和接口进行分类。
包的概念跟操作系统里的文件夹类似,我们把写完的Java类文件放到不同的文件夹(包)中。
包与文件夹一样,也会有层次结构,一个包中可以包含若干个子包,子包再还可以包含若干个子包。
不同的包下,文件命名可以重名。
与包相关的语句只有2个,一个是打包语句package,一个是引包语句import。

package语句
语法:
package 包名;
package 包名.子包名.子子包名;

package com.webi.hwj.weixin.controller;

public class WeixinHandleController {

}

表示当前类被放在了com.webi.hwj.weixin.controller包中。
package语句必须写在类的第一行。
package语句也可以不写,不写的话则默认将当前类放在默认路径下,也就是src根目录下。

import语句
当在类中,需要访问一些第三方或者放在其他包中的类时,那我们必须使用import语句,把这个类给先导入过来。
import之后,才能在本类中使用。如果这个类与本类在一个包内则不需要import。
通过import xx.*;可以导入当前包下的所有类,但不建议这么做,因为性能问题。

语法:
import 包名;
import 包名.子包名.子子包名;

package com.webi.hwj.weixin.controller;

import org.apache.log4j.Logger;

public class WeixinHandleController {
  Logger logger = Logger.getLogger(WeixinHandleController.class);
}

java常用包
java.lang:Java语言包,任何Java类中,该包都被自动导入,例如:System,String等。
java.sql:数据库访问包。
java.io:这个包由对您的输入/输出操作有用的类组成。
java.util:该包提供了许多创建如:list, calendar, date等所需要的类和接口。
java.net:该包提供了许多进行TCP/IP网络编程的类和接口。

封装(Encapsulation)
将对象的实现细节(属性&方法)隐藏起来,然后通过一些公用方法来暴露该对象的功能。
将对象的状态信息隐藏在对象内部,不允许外部程序直接访问对象的内部信息,
而是通过该类所提供的方法来实现对内部信息的操作与访问。

封装可以被认为是一个保护屏障,防止该类的代码和数据被外部类定义的代码随机访问。
要访问该类的代码和数据,必须通过严格的入口控制。
适当的封装可以让代码更容易理解与维护,也加强了代码的安全性。

例如:
~电视机的内部是什么电路你也不知道,你只知道用遥控器能操作电视就可以了。
~女生的年龄是比较隐私的,需要隐藏起来,不要让外人知道。
~吃一粒胶囊的时候,你不用care胶囊里有多少种化学药品,只管吃了能治病就行。

那么为什么要隐藏呢?
开发者让使用者只能通过事先定制好的方法来访问数据,可以方便地加入控制逻辑,限制对属性的不合理操作。
把例子写一下,有墙有门。。。。!!!

那么怎么隐藏呢?

Java的访问控制符(体现了Java的封装性)
可修饰在类名、方法名、成员变量名上等等。
级别从小到大:private->default->protected->public
 privatedefault(啥也不写)protectedpublic
同一类中yesyesyesyes
同一包中 yesyesyes
子类中  yesyes
全局范围(同一项目)   yes
(可以结合上面的表自己做几个小例子,加深印象。)



注意:
~类的控制级别只有public与default,而成员变量与方法则4个都有。
~如果类的构造方法为private,则其他类均不能生成该类的实例(自己类内部则可以)。
~Java类中一般会使用private来修饰成员变量,来防止外部调用。(其实还有一个好处,生成getter/setter方法用于多态时的覆盖属性值)

class Test{
  private String name;
  private int age;

  public Test(){
    this.name = "张三";
    this.age = 18;
  }

  public void speak(){
    System.out.println("大声说出我的名字是"+name);
    System.out.println("大声说出我的年龄是"+age);
  }
}

class Main{
  public static void main(String[] args){
    Test t1 = new Test();
    t1.name = "张三";//编译报错,name属性已经被封装,无法调用。
    t1.speak();//程序作者只允许调用speak方法。
  }

}

这段代码中,将 name 和 age 属性设置为私有的,只能本类才能访问,其他类都访问不了,如此就对信息进行了隐藏。

模块设计追求高内聚、低耦合。
高内聚:尽可能把模块的内部数据、功能实现细节隐藏在模块的内部独立完成,不允许外部直接干涉。
低耦合:仅暴露少量的方法给外部使用。
(有兴趣的同学可以看一下单例模式。。。)

方法的覆盖
重写(overriding)
在父子类继承的情况下,子类写出一个跟父类一模一样的方法,方法体内可以修改,就叫方法重写。
重写的好处在于子类可以根据需要,定义特定于自己的行为。 也就是说子类能够根据需要实现父类的方法。

注意:
子类覆盖方法的访问权限要不小于父类中被覆盖方法的访问权限。
构造方法不能被重写。

class Person{
  public void eat(){
    System.out.println("爸爸在吃饭");
  }
}

class Driver extends Person{
  public void eat(){
    System.out.println("儿子在吃饭");
  }
}

重载(overloading)
在自己的类中,一个新写的方法,方法名跟某一个老方法相同,但是参数必须不一样,返回值有可能也会不一样,
就叫方法重载,新方法重载了老方法。

class Person{
  public void eat(){
    System.out.println("吃饭");
  }
  public void eat(String name){
    System.out.println(name+"在吃饭");
  }
}

比较常见的重载方式是重载构造方法。

public class Person{
  private String name;
  public Person(){

  }

  public Person(String name){
    this.name = name;
  }
}

多态(Polymorphism)
多态是建立在继承的基础上的,是指子类类型的对象可以赋值给父类类型的引用变量,但运行时仍表现子类的行为特征。也就是说,同一种类型的对象执行同一个方法时可以表现出不同的行为特征。

java引用变量有两个类型:一个是编译时类型,一个是运行时类型。编译时类型由声明该变量时使用的类型决定,
运行时类型由实际赋给该变量的对象决定,如果编译时类型与运行时类型不一致,就可能会出现所谓的多态。

多态的三个必要条件:
~要有类继承或实现接口(接口在下面会讲解)
~要有方法重写
~父类引用指向子类对象(或接口引用指向实现类对象)。

一旦满足以上3个条件,当调用父类中被重写的方法后,运行时创建的是哪个子类的对象,
就调用该子类中重写的那个方法。

生活中的例子:
按键盘F1键
如果当前在 Word 下弹出的就是 Word 帮助;
在 Windows 下弹出的就是 Windows 帮助和支持。
在XX游戏中就是人物介绍。

同一个事件发生在不同的对象上会产生不同的结果。

多态性是对象多种表现形式的体现。

class Father{
  public void somke(){
    System.out.println("抽中华");
  }
}

class Son extends Father{
  public void somke(){
   System.out.println("抽中南海");
  }
}

class Main{
  public static void main(String[] argus){
    Father f = new Son();//子类对象可以直接赋给父类变量
    f.somke();//运行时表现出子类的行为特征
    //这意味着同一个类型的对象在执行同一个方法时,可能表现出多种行为特征。
  }

}

为什么要使用多态?
可以增强程序的可扩展性及可维护性,使代码更加简洁。
作用:在方法传递引用数据类型的时候,使方法内部的代码更具有可维护性,当有新增子类时,
方法内部的代码不用去修改,进一步的使用抽象的概念。

//例如父类是Father,其子类分别是Son1、Son2、Son3
public static void doSomething(Father f){
 

//三个儿子都可以当参数传进来,三次调用的work方法表现出来的行为都不一样。
  f.work();
}

public void main(String[] args){
doSomething(new Son1());
doSomething(new Son2());
doSomething(new Son3()); 
  //再增加Son4的时候,doSomething方法也不用修改。
}

多态与继承、方法重写密切相关,我们在方法中接收父类类型作为参数,在方法实现中调用父类类型的各种方法。
当把子类作为参数传递给这个方法时,java虚拟机会根据实际创建的对象类型,
调用子类中相应的方法(存在方法重写时)。

大白话:
在父子类继承/接口环境中,去实例化对象时,数据类型使用的是父类/接口,实例化的是子类/接口实现类的对象。
多态就是一个数据类型的对象会具有多种状态/形态,具有多种实现方式。

注意事项(面试题):
在多态情况下,父类的成员变量无法被子类覆盖,因此需要使用setter/getter方法。
在继承情况下,父类的成员变量可以被子类覆盖。
在多态情况下,子类有重写父类方法的情况,则调用子类方法。

class Father {
  String name = "爸爸";
}

class Son extends Father {
  String name = "儿子";
}

public class Main {
  public static void main(String[] args) {
    Son s = new Son();
    Father f = new Son();

   

System.out.println(s.name); //输出儿子
    System.out.println(f.name); //输出爸爸
  }
}

多态情况下,对象无法调用子类独有的方法与属性,只能调用编译类型的成员。

class Father {
  String name = "爸爸";
}

class Son extends Father {
  String name = "儿子";
  int age = 18;//子类特有的属性

  public void eat(){
    System.out.println("儿子在吃饭");//子类特有的方法
  }
}

public class Main {
  public static void main(String[] args) {
    Father f = new Son();
    System.out.println(f.name);//正常输出
    System.out.println(f.age);//编译出错
    System.out.println(f.eat());//编译出错
  }
}

抽象类(abstract class)与抽象方法(abstract method)
抽象方法
可以只有方法定义,没有方法实现的方法,抽象方法由子类来实现。
子类必须实现父类的抽象方法,否则报错,但如果子类也是抽象类就没问题了。

语法:
[修饰符] abstract 返回类型 方法名(参数列表);

抽象类
抽象类是从多个具体类中抽象出来的父类,它具有更高层次的抽象。从多个具有相同特征的类中抽象出一个抽象类,
以这个抽象类作为其子类的模板,从而避免了子类设计的随意性。

抽象类体现的就是一种模板模式的设计,抽象类做为多个子类的通用模板,子类在抽象类的基础上进行扩展、改造。

语法:
[修饰符] abstract class 类名{
  
}

例子:

abstract

class A{
  String name;
 

abstract
int method1(int a,int b);
  void hello(){
    System.out.println("hello~~~");
  }
}

应用场景:

abstract class Father {
  String name;

 

public abstract void eat();

  public void sleep() {
    System.out.println("爸爸在睡觉然后...");
    eat();//父类方法想调用子类的实现方法,但父类的方法只是一个定义,是空的。
    System.out.println("爸爸要走了");
  }
}

class Son extends Father {
  public void eat() {
    System.out.println("儿子在吃饭");
  }
}

class Son2 extends Father {
  public void eat() {
    System.out.println("儿子在吃饭2222");
  }
}

class Main {
  public static void main(String[] args) {
    Father f = new Son();
    Son s = new Son();

    f.sleep();
    s.sleep();

    Father f2 = new Son2();
    Son2 s2 = new Son2();
    f2.sleep();
    s2.sleep();
  }
}

注意事项
~抽象类不能被实例化,不能new,抽象类的构造方法不能用于创建实例,而是用于被子类调用。
~static&final&private方法与构造方法不能被声明为抽象方法。
~抽象类中既可以有正常方法,也可以有抽象方法,甚至完全没有抽象方法也行,但有抽象方法的类必须是抽象类。
~抽象类允许有构造方法,在子类实例化对象时会自动调用父类的构造方法。
接口(interface)
接口是抽象类的变体,在接口中,所有方法都是抽象的,多继承性可通过实现这样的接口而获得。
接口就是标准(一些类需要遵守的规范),是用来隔离具体实现的(或者说是和具体实现解耦)。

举个生活中的例子:
各种电脑、移动硬盘等设备上的USB接口就是标准,大家各自制造自己的具体产品。
产品使用者和提供者都遵守这个标准,
那么使用者就不必担心自己电脑上的USB接口是否只能插移动硬盘而不能插手机。

再打个比方,网络上的各种协议,比如HTTP协议,只要客户端和服务端都遵守这个协议,
那么无论是用火狐狸浏览器还是用IE,也或者是360浏览器,都可以访问,不用担心服务端发过来的信息,
浏览器解析不了。

回到主题,程序接口的使用就将调用者和提供者之间进行了解耦,只要实现者遵守这个接口来做实现就好,
实现细节不用管。

语法:
[修饰符] interface 接口名{
  ...
}

注意事项
~接口不是类,不能被实例化也没有构造方法,但抽象类有构造方法。
~接口内所有的属性均默认为public static final,只能在定义时指定默认值。
~接口内所有的方法均默认为public abstract,不能用static&final。
~接口内不允许有正常方法(带方法体的方法)。
~接口可以同时继承多个父接口,但接口只能继承接口,不能继承类。
~方法提供者与方法调用者有可能是一个人也可能是不同的人。
~一个java源文件中最多只能有一个public接口,并且文件名与接口名相同。

类实现接口(implements)
类一旦实现接口,就必须实现其所有方法,否则这个类必须声明为抽象类。
一个类可以只能继承一个类,但可以实现多个接口。
多个无关的类可以实现一个接口,一个类可以实现多个无关的接口。
一个类可以在继承一个父类的同时,实现一个或多个接口。

public class A extend B implements C,D{
  ...
}

接口与抽象类的区别(经典初级Java面试题)
接口不能含有任何非抽象方法,而抽象类可以。(JDK8以及以上除外)
类可以实现多个接口,但只能继承一个父类。
接口不是类分级结构的一部分,没有联系的类可以实现相同的接口。

标记接口
最常用的继承接口是没有包含任何方法的接口。
标识接口是没有任何方法和属性的接口.它仅仅表明它的类属于一个特定的类型,供其他代码来测试允许做一些事情。
标识接口作用:简单形象的说就是给某个对象打个标(盖个戳),使对象拥有某个或某些特权。
例如:java.awt.event 包中的 MouseListener 接口继承的 java.util.EventListener 接口定义如下:

package java.util;
public interface EventListener{}

没有任何方法的接口被称为标记接口。标记接口主要用于以下两种目的:

建立一个公共的父接口:
正如EventListener接口,这是由几十个其他接口扩展的Java API,你可以使用一个标记接口来建立一组接口的父接口。
例如:当一个接口继承了EventListener接口,Java虚拟机(JVM)就知道该接口将要被用于一个事件的代理方案。

向一个类添加数据类型:
这种情况是标记接口最初的目的,实现标记接口的类不需要定义任何接口方法(因为标记接口根本就没有方法),
但是该类通过多态性变成一个接口类型。

接口的好处(接口的应用场景)
很多小伙伴不知道接口的好处是什么,包括我自己刚开始也不是很清楚,接口可以配合多态性彰显巨大威力。
在定义方法的参数类型与返回类型时,以及定义成员变量的类型时都可以使用接口,
这样你传进任何这个接口的实现类的对象都可以,返回、成员变量都同理。

如果方法的参数只写死一个类,那么我们只能传这个固定类的对象,这种写法就非常的死!!!
限制住了调用者传递的对象必须只能是这个固定的类,无法扩展自己的自定义类对象传到方法中。

public void eat(Person person){
  ...
}

eat(new Person());

下面升级一下,我们使用抽象类&继承。
这样貌似解决了上面只能传一个类的对象的尴尬局面,看~这次我可以传多个类,只要是Father的子类就好了。
但不要忘了,Java只支持类的单继承,很多场景,要传的类的对象已经有父类,那你就瞎了。
如果这个方法只能传Father的子类,也有些不够灵活。

public void eat(Father f){
  ...
}

eat(new Son());
eat(new Son2());
eat(new Son3());

再升级,我们使用接口,这次所有实现该接口的类的对象都可以通过参数传过来,并且别忘了,
实现接口的类可以是毫无关联的,类与类之间没有关系,并且每个类允许实现多个接口。
这样是不是就更灵活更随便了呢?
还有一点,当传过来的参数的对象变化多端,只要你实现了接口,我就放你进来。
那么这个被调用的方法是不用修改的。

public void eat(Tag tag){
  ...
}

eat(new Person());
eat(new Father());
eat(new Tiger());

方法的编写者需要调用参数对象的一个方法,但是他不管这个方法的实现,实现由调用者负责。

所谓面向接口编程,让程序依赖于一个比较宽泛的(或者说更抽象的)类型,这个类型下面应该有很多具体的子类;也就是说,这个类型处在一个比较大的类型树的顶端。

“请求”和“实现”分离开来,这就是接口的解耦!

使用接口,可以避免接口调用方直接参与业务逻辑实现,所以能避免接口调用与业务逻辑实现紧密关联,即解耦

一个现实的例子(新手PASS):
Collection接口定义了iterator()方法,该方法返回Iterator接口的实现类对象,
下面这一段代码可以遍历所有直接或间接实现Collection接口的集合类:

public void traverse(Collection<?> c) {
    Iterator<?> i = c.iterator();
    while(i.hasNext()) {
        Object o = i.next();
        //do something with o...
    }
}

也就是说,这个方法无论传入ArrayList、LinkedList、HashSet、TreeSet、ArrayBlockingQueue还是其他的什么Collection实现,都可以遍历其中的内容。
如果没有接口的话,那么就需要写一堆traverse方法了,traverse(ArrayList<?> list)、traverse(LinkedList<?> list) ……

一个还不错的例子:

定义一个接口 磁盘
interface Disk(){
  void save(File file);
}

U盘和硬盘都是磁盘,都实现这个接口
class UDisk implement Disk{
void save(File file){
   System.out.println("u盘在存储");
 }
}

class HardDisk implement Disk{
void save(File file){
  System.out.println("硬盘在存储");
}
}

一个需要用磁盘来存储的下载工具
class Download{
  Disk disk;//用接口声明,我们不知道,也不用知道,我们未来会存到什么样的磁盘,我们不依赖于任何类型的磁盘,我们只依赖于这个接口

  void download(File file){
       disk.save(file);
  }

  void setDisk(Disk disk){
  this.disk=disk;
  }

  public static void main(String[] args){
  Download download = new Download();
  设置存储目标为U盘
  download.setDisk(new UDisk());
  文件被存到了U盘
  download.download(file);

  设置存储目标为硬盘
  download.setDisk(new HardDisk());
  文件被存到了硬盘
  download.download(file);
  }
}

某天我们想把下载的文件保存到CD里边,我们只需要定义CDDisk类,实现Disk接口就可以不对download本身做任何修改,就可以方便的将文件下载到CD或其他介质里。我们的Download类不依赖于任何具体的类,这样就接触了与任何具体存储设备的耦合!

这样就接解除了与任何具体存储设备的耦合!
如果是依赖接口,则可以随心所欲的更换实现类。

就像SpringJDBC框架设计的dataSource注入 (新手PASS)
不同的数据库厂商只要把自己的代码都实现dataSource接口就可以了。
这样无论你要切换哪个厂商的代码,至少底层框架中的dataSource是不会改变的,框架的代码是不会变的,
只是改变注入就可以。 这样岂不是很爽?

引用数据类型的转换
向上转型
子类转换为父类,自动转换(前提是具有继承或实现关系)。



Father f1 = new Son();

向下转型
需要强制转换,将父类对象显示的转换成子类类型。

Father f1 = new Father();
Son s1 = (Son)f1;需要式

instanceof运算符
判断一个对象的类是否实现了某个接口
判断一个对象是否属于一个类
它的返回值是boolean型的
语法:
对象 instanceof 接口
对象 instanceof 类

A a = new B();
C c = new C();
System.out.println(a instanceof B);//true
System.out.println(a instanceof A);//true
System.out.println(c instanceof A);//false
System.out.println(c instanceof B);//编译出错

内部类(新手PASS)
内部类(嵌套类)
内部类就是定义在另一个类内部的类。
内部类对于同一包中的其它类来说,内部类能够隐藏起来。

注意:
内部类可以访问其外部类中所有的属性和方法
无需创建外部类的对象,即可从内部类访问外部类的变量和方法。
必须创建内部类的对象,否则无法从外部类访问内部类的变量和方法。

如果内部类中有和外部类同名的变量或方法,则内部类的变量和方法将获得比外部类的变量和方法更高的优先级。

不能定义static变量

public class Outer {
  private int varOuter=100;
  class Inner {
    int varInner=200;
    public void showOuter() {
      System.out.println(varOuter); //是否能够输出?
    }
  }

  public void showInner() {
      Inner i=new Inner();  
      System.out.println(i.varInner);
  }
}

普通类的访问权限修饰符default public
内部类的访问权限修饰符default public protected private

内部类的访问
在Outer内访问Inner,只需如下:
Inner in = new Inner() ;

在Outer外访问Inner,必须如下:
Outer o = new Outer(); //实例化外部类
Outer.Inner oi = o.new Inner(); //实例化内部类

静态内部类
用static标识的内部类为静态内部类。
静态内部类作为外部类的静态成员,不能访问外部类非静态成员。
非静态内部类只能定义非静态成员,而静态内部类可以定义静态成员和非静态成员。
使用Outer.Inner inn=new Outer.Inner()方式实例化静态内部类。
非静态内部类不可以使用上面的方式实例化

局部内部类
在一个类的方法体中或程序块内定义的内部类

类中定义的内部类

class  A{
  int a; 
  public void method(){

  }   

  class B{

  }
}

局部内部类

class A{
  int a;
  public void method(int c){
    int b=0;
    class B{
    
    }
  }
}

在方法定义的内部类中只能访问方法中的final类型的局部变量

public class Outer2 {
  public int a = 1;
  private int b = 2;
  public void method(final int c) {
    int d = 3;
    final int e = 2;
    class Inner {
      private void iMethod(int e) {
        //System.out.println(e);
      }
    }
  }
}

总结
今天我们主要学习了java面向对象的进阶部分,包括传说中的封装、继承、多态等等知识,尤其是关于接口的运用,
一些小伙伴都不是很理解为什么要使用接口,概念在于理解,而不是死记硬背,这样才能融会贯通。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: