Java自学总结4

一、多态

1.多态的概念

多态是面向对象的重要特征,简单地说即“一个接口,多种实现”,就是同一种事物表现出的多种形态。
编程其实就是一个将具体世界进行抽象化的过程,多态就是抽象化的一种体现,把一系列具体事物的共同点抽象出来,再通过这狗抽象的事物,与不同的具体事物进行对话。
对不同类的对象发出相同的消息将会有不同的行为。例如,公司的老板让所有员工在九点开始工作,他只要在九点钟的时候说“开始工作”即可,而不需要对销售人员说“开始销售工作”,对技术人员说“开始技术工作”。因为“员工”是一个抽象的事物‘只要是员工就可以开始工作,他知道这一点就行了。至于每个员工,当然会各司其职,做各自的工作。
多态允许将子类的对象当作父类的对象使用,某父类型的引用指向其子类型的对象,调用的方法是该子类型的方法。这里引用和调用方法的代码编译前就已经决定了,而引用所指向的对象可以在运行期间动态绑定。
再举个比较形象的例子:有一个函数是叫某个人来吃饭,函数要求传递的参数是人的对象,可是来了一个美国人,你看到的可能是用刀和叉子在吃饭,来了一个中国人,你看到的可能是用筷子在吃饭,这就体现出了同样是一个方法,却产生了不同的形态,这就是多态。

2.多态的实现

(1)实现条件

Java实现多态有3个必要条件:继承、重写和向上转型。
[1] 继承:在多态中必须存在有继承关系的子类和父类。
[2]重写:子类对父类中某些方法进行重新定义,再调用这些方法时就会调用子类的方法。
[3]向上转型:在多态中需要将子类的引用赋给父类对象,只有这样该引用才能够具备调用父类的方法和子类的方法。
只有满足了上述3个条件,才能够在同一继承结构中使用统一的逻辑实现代码处理不同的对象,从而达到执行不同行为的目的。
对于Java而言,它多态的实现机制遵循一个原则:当超类对象引用变量引用子类对象时,被引用对象的类型而不是引用变量的类型决定了调用谁的成员方法,但是这个被调用的方法必须是在超类中定义过的,也就是说被子类覆盖的方法。

(2)实现形式

在Java中有两种形式可以实现多态,即继承和接口。
【1】基于继承实现的多态
基于继承的实现机制主要表现在父类和继承该父类的一个或多个子类对某些方法的重写,多个类对同一方法的重写可以表现出不同的行为。
模拟USB设备的使用。定义父类USB,子类UDisk、Umouse和UKeyboard对父类的方法重写,表现出各自的行为特征。

package duke.example.ch4;

public class USB{                       //定义USB类
     private String name;
     public String getName(){
          return name;
     }
     public void setName(String name){
          this.name = name;
          }
     public String load(){              //装载USB的方法
          return "USB设备正在装载...";
          }
}
 public class UDisk extends USB{        //定义一个USB设备U盘
      public UDisk(){
           setName("16G U盘");
           }
      public String load(){             //重写父类装载USB的方法,实现多态
           return"正在装载的是:" + getName();
           }
}

public class Umouse extends USB{        //定义一个USB设备 USB鼠标
     public Umouse(){
          setName("USB接口鼠标")}
          public String load(){         //重写父类装载USB的方法,实现多态
               return"正在装载的是:" + getName();
               }
}

public class UKeyboard extends USB{     //定义一个USB设备 USB键盘
     public UKeyboard(){
          setName("USB接口键盘")}
         public String load(){          //重写父类装载USB的方法,实现多态
               return"正在装载的是:" + getName();
               }
}

public class USBTest{                   //测试程序
     public static void main(String[] args){
          USB udisk = new UDisk();      //父类USB引用子类UDisk对象
          System.out.println(udisk.load());

          USB umouse = mew Umouse();    //父类USB引用子类Umouse对象
          System.out.println(umouse.load());

          USB ukeyboard = new Ukeyboard();  //父类USB引用子类UKeyboard对象                 
          System.out.println(ukeyboard.load());
          }    
}     

在上面的代码中,UDisk、Umouse和UKeyboard继承USB类,并且重写了load()方法,程序运行结果是调用子类中的方法,输出UDisk、Umouse和UKeyboard的名称,这就是多态的表现。不同的对象可以执行相同的行为,但是它们都需要通过自己的实现方式来执行,这就要得益于向上转型了。
Object、USB和UDisk三者继承链关系是UDisk→USB→Objec。所以也可以这样说:当子类重写父类的方法被调用时,只有对象继承链中的最末端的方法才会被调用。
因此基于继承实现的多态可以总结如下:对于引用子类的父类类型,在处理该引用时,它适用于继承该父类的所有子类,子类对象不同,对方法的实现就不同,执行相同动作产生的行为也就不同。
如果父类是抽象类,那么子类必须要实现父类中所有的抽象方法,这样该父类所有的子类一定存在统一的对外接口,但其内部的具体实现可以各异。这样我们就可以使用顶层类提供的统一接口来处理该层的方法。

【2】基于接口实现的多态
接口的灵活性就在于“规定一个类必须做什么,而不管你如何做”。可以定义一个接口类型的引用变量来引用实现接口的类的实例,当这个引用调用方法时,它会根据实际引用的类的实例来判断具体调用哪个方法,这和上述的父类对象引用访问子类对象的机制相似。
与接口相比继承都是单继承,只能为一组相关的类提供一致的服务接口;但是接口可以是多继承多实现,能够利用一组相关或者不相关的接口进行组合与扩充,能够对外提供一致的服务接口,所以接口相对于继承来说有更好的灵活性。
例:模拟游戏中攻击的设计,要求只要发出进攻的指令,士兵就用自动步枪射出达姆弹,坦克立即发射破甲弹,F35歼击机马上发射空空导弹。我们秩序设计进攻的接口,定义进攻的指令方法,各种进攻手段只要各自实现这个接口的方法,利用多态技术就可以完成各自的进攻任务。

package duke.example.ch4;

public interface IFight{                //定义进攻的接口
     public void attack(String order);  //进攻指令
}     
public class Soldier implements IFight{ //士兵实现进攻的接口
     @Override
     public void attack(String order){  //实现攻击指令
          System.out.println(order + "发射达姆弹...");
     }
}

public class Tank implements IFight{    //坦克实现进攻的接口
     @Override
     public void attack(String order){  //实现攻击指令
          System.out.println(order + "发射破甲弹...");
          }
}

public class F35 implements IFight{     //战斗机实现进攻的接口
     @Override
     public void attack(String order){  //实现攻击指令
          System.out.println(order + "发射AIM-120型中程空空导弹...");
          }
}

public class FightTest{                 //测试类
     public static void main(String[] ards){
          IFight soldier = new Soldier();  //父类引用指向子类对象
          soldier.attack("海豹突击队");
          
          IFight tank = new Tank();        //父类引用指向子类对象
          tank.attack("MIA3新型主战坦克");

          IFight f35 = new F35();
          f35.attack("F35隐身歼击机");
          }
}                              

这种父类引用指向子类对象可以降低程序的耦合性,被调用对象对于调用者完全是透明的。父类对象向下转型,它只能调用父类已存在的接口,子类可以对接口编程,有不同的实现,而父类对象不需要关心它的实现。这对于程序的可维护性和可扩展性有很大的帮助,如本例中,我们要增加导弹这个武器,只需导弹类实现IFight的接口就可以,其它地方不用改动。如果结合工厂模式、Java反射机制等,就能够实现很多强大的功能。
运行时多态性是面向对象程序设计代码重用的一个最强大机制,动态性的概念也可以被说成“一个接口,多个方法”。Java实现运行时多态性的基础时动态方法调度,它是一种在运行时而不是在编译期调用重载方法的机制。

二、字符串类

字符串是由0个或多个字符构成的一个集合,是学习Java的先行必备知识。在Java语言中,字符串是按照对象来进行处理的。java.lang包中为编程人员提供了String类和StringBuffer类来处理字符串。Sting类用于处理“不可变”的字符串;StringBuffer类则用于处理“可变”的字符串。

1.字符串对象

【1】声明字符串
声明字符串的格式:

         String 对象名[=null];

【2】创建字符串
(1)初始化
像基本数据一样,在声明字符串对象的同时为其赋起始值。例如:

      String s="Java";

(2)构造方法
String()构造方法
该构造方法可以创建新的字符串对象,是字符串类的默认构造方法,不接受任何参数,构造的对象是一个空字符串。语法如下:

        new String();

例如:

      "like"+new String+"Java"

结果:“like Java”。

public String(String original)构造方法
该构造方法使用与参数相同的字符序列构造一个新的字符串对象。例如:

  String s=new String("Java");

String(byte[] bytes)构造方法
该构造方法通过使用平台的默认字符集解码指定的byte数组(字节数组)构造一个新的字符串对象,新字符串的长度不一定等于byte数组的长度。语法如下:

      newString(byteArr);
      byte[] byteArr=new byte[]{74,97,118,97};
      String s=new String(beteArr);

结果:“Java”。

public String(char[] value)构造方法
该构造方法使用字符数组中包含的字符序列去构造一个新的字符串对象。语法如下:

     new String(charArr);
     char[] charArr=newchar[]{'L','o','v','e','','J','a','v','a'};
     String s=newString(charArr);

结果:“Love Java”。

public String(StringBuffer buffer)和public String(StringBuilder builder)构造方法。这两个构造方法都可以构造一个字符串对象,前者生成字符串的内容为字符串缓冲区中包含的字符序列;后者为字符串生成的字符序列。

【3】引用字符串
字符串常量是对象,因此可以把字符串常量的引用赋值给一个字符串对象。例如:

    String s1,s2;
    s1="Do you like Java?";
    s2="I like.";

构造字符串对象

package duke.example.ch5;

public class StringCreat{
     public static void main(String[] args){
          String s1;
          s1="I";
          char[] charArr=new char[]{'L','o','v','e'};
          String s2=new String(charArr);
          byte[] byteArr=new byte[]{74,97,118,97};
          String s3=new String(byteArr);
          System.out.println(s1 + s2 + s3 + "!");
          }
}          

输出结果为:I Love Java!

三、关键字

一、概念

Java关键字(Key Word): 对Java的编译器有特殊的意义,他们用来表示一种数据类型或者表示程序的结构.

保留字(Reserve Word):即它们在Java现有版本中没有特殊含义,以后版本可能会作为有特殊含义的词,或者该词虽然在Java中没有特殊含义,以后版本也不打算使用,但在其它语言中有特殊含义,不宜在Java中定义为变量名称等,因为容易混淆。

注意:关键字和保留字均不能用作变量名、方法名、类名、包名和参数。

二、具体的保留字

goto、const

三、具体的关键字(51个)

1.访问修饰符(3个)

public、protected、private

作用:用来修饰类(接口、抽象类)、方法、属性、构造方法、常量、主函数

2.类、接口、抽象类(9个)

class、interface、abstract——定义

extends——继承类、implements——实现接口

new——新建一个对象、super——调用父类方法、this——指代当前对象

instanceof——通过返回一个布尔值来指出,这个对象是否是这个特定类或者是它的子类的一个实例。

3.数据类型(13个)

void——没有返回值

byte、short、int、long——整型数据

float、double——浮点型数据

char——字符型数据

boolean——判断型数据

enum——枚举

null、true、false——值类型

4.线程(2个)

synchronized——线程同步(修饰方法、代码块,方法、代码块的同步)

volatile——线程同步(修饰属性,属性的同步)

5.异常(5个)

throw——抛出方法代码中的异常给方法自身。使用位置:方法中间

throws——抛出方法中的异常给调用者。使用位置:方法外部

try——捕获{}中代码是否有发生异常

catch——处理try捕获的异常

finally——不管有没有异常发生都会执行的代码块

6.返回(1个)

return

7.循环、条件(10个)

if、else、switch、case、break、default、continue、while、do、for

8.包(2个)

package、import

9.瞬时的(1个)

transient

10.断言(1个)

assert

11.调用底层代码(C\C++)(1个)

native

12、不可变的——final(1个)

修饰属性、常量、局部变量、参数——作用:数据是不可改变的

修饰类——作用:修饰的类不能被继承

修饰普通方法——作用:修饰的方法不能被重写

13.静态的——static(1个)

概述:

当static修饰类的属性或者方法时,那么就可以在没有创建对象的情况下使用该属性或方法。

静态块也是static的一个应用,用于初始化类时的一些操作。

静态方法和静态变量

被static修饰后的属性或者方法,使用时不需要new 一个类,用类.属性名或方法名访问.

比如java.lang.Math就存放了很多静态资源,可以直接使用Math.random()来获取随机数.

一些需要注意的地方

非静态方法是可以访问静态资源的,

静态方法是不能引用非静态资源的。

来看一个代码实例:

1 public class TestStatic {
2 
3     protected int i = 100;
4 
5     public static void main(String args[]){
6         System.out.println(i);
7     }
8 }

在以上代码,编译的时候会出错,main方法是静态方法,变量i是非静态的。

解决办法是,将变量i加上static修饰。

不经就要提出一个问题,

为什么非静态方法可以访问静态资源,而静态方法不能访问非静态资源呢?

从类加载机制上讲,静态资源是类初始化的时候加载的,然后非静态资源是new一个该类的对象的时候加载的。

这就带来一个问题:

加载类时默认先加载静态资源的,当new一个对象之后,才会加载其他资源,所以在new对象之前,静态资源是不知道类有哪些非静态资源的,

但是当对象new出来之后,该类的所有属性和方法都知道。

还有需要注意的是:

1.静态属性和方法可以通过类.属性名或方法名,而且,该类的对象也是访问静态属性和变量的。

2.Java的语法规定,static不能修饰局部变量。没有为什么,这就是规定。

静态块
静态块和静态变量、静态方法是没什么区别的,也是在类加载的时候执行,而且只执行一次。

关于静态块有两点需要注意:

1.静态资源的加载顺序严格按照静态资源的定义顺序加载的

2.静态块,对于定义在它之后的静态变量,可以赋值但不能访问。

static的题目
下面main()方法的输出结果是什么:

public class InstanceClass extends ParentClass{

    public static String subStaticField = "子类静态变量";
    public String subField = "子类非静态变量";
    public static StaticClass staticClass = new StaticClass("子类");

    static {
        System.out.println("子类 静态块初始化");
    }

    {
        System.out.println("子类 [非]静态块初始化");
    }

    public InstanceClass(){
        System.out.println("子类构造器初始化");
    }

    public static void main(String args[]) throws InterruptedException {
        new InstanceClass();
    }
}

class ParentClass{
    public static String parentStaticField = "父类静态变量";
    public String parentField = "父类[非]静态变量";
    public static StaticClass staticClass = new StaticClass("父类");

    static {
        System.out.println("父类 静态块初始化");
    }

    {
        System.out.println("父类 [非]静态块初始化");
    }

    public ParentClass(){
        System.out.println("父类  构造器初始化");
    }
}

class StaticClass{
    public StaticClass(String name){
        System.out.println(name+" 静态变量加载");
    }
}

输出结果:

 View Code

下面是我总结类加载流程,可以对照着这个流程,可以再重新看一下上面的例子,会有新的理解。

1. 加载父类静态
    1.1 为静态属性分配存储空间并赋初始值
    1.2 执行静态初始化块和静态初始化语句(从上至下)

2. 加载子类静态
    2.1 为静态属性分配存储空间
    2.2 执行静态初始化块和静态初始化语句(从上至下)

3. 加载父类非静态
    3.1 为非静态块分配空间  
    3.2 执行非静态块

4. 加载子类非静态
    4.1 为非静态块分配空间  
    4.2 执行非静态块

5. 加载父类构造器
    5.1 为实例属性分配存数空间并赋初始值
    5.2 执行实例初始化块和实例初始化语句
    5.3 执行构造器内容

6. 加载子类构造器
    6.1 为实例属性分配存数空间并赋初始值
    6.2 执行实例初始化块和实例初始化语句
    6.3 执行构造器内容

一步一步地解析:

在TestStaticLoad 的main方法中,执行了new God(),那就就会去加载God类,在这之前会先加载它的父类:TestStaticLoad
第一步:加载父类静态,执行System.out.println(“TestStaticLoad static”); 输出:TestStaticLoad static,
第二步:加载子类静态,执行System.out.println(“God static”);,输出God static
第三步:加载父类非静态,Person person = new Person(“TestStaticLoad”);,这里实例化了Person 对象,那就会去加载Person类。
第四步:加载Person类,首先看有没有父类,没有。好,加载静态块,执行System.out.println(“person static”);输出person static
第五步:Pernson类静态块加载完毕,加载构造器,new一个Person对象,输出person TestStaticLoad。这时TestStaticLoad 类非静态块加载完毕
第六步:加载God 父类(TestStaticLoad )构造器,输出TestStaticLoad constructor
第七步:God父类全部加载完毕,加载God的非静态块,Person person = new Person(“God”);这时又会去加载Person类,需要注意的是,static块只加载一次,因为之前在父类已经加载过了,这时只加载构造器,输出person God
最后一步:加载本类God 的构造器,输出God constructor。

static关键字的总结:
static关键字 可以再没有创建对象的时候进行调用类的元素
static 可以修饰类的方法 以及类的变量, 以及静态代码块
被static修饰的成为静态方法,静态方法是没有this的,静态方法不能访问同一个类中的非静态方法和静态变量,但是非静态方法 可以可以访问静态变量
类的构造器 也是静态的
静态变量被所有的内存所有的对象共享,在内存中只有一个副本。非静态变量是是在创建对象的时候初始化的,存在多个副本,每个副本不受影响。
static 静态代码块,static 代码块可以放在类中的任何地方,类加载的时候会按照static代码块的顺序来加载代码块,并且只会执行一次。
枚举类和静态代码块 赋值静态代码块的变量
非静态方法能够通过this访问静态变量
静态成员变量虽然独立于对象,但是不代表不可以通过对象去访问,所有的静态方法和静态变量都可以通过对象访问。
static不可以修饰局部变量(java语法规定)
没想到static能有这么多需要注意的,可以说Java中的语法还是有很多可以深究的.

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

一点酸儿

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

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

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

打赏作者

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

抵扣说明:

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

余额充值