java基础

数据类型

  • 数据类型分类
    基本数据类型:包括 整数 、 浮点数 、 字符 、 布尔
    引用数据类型:包括 类 、 数组 、 接口
    基本数据类型:四类八种

    数据类型 关键字 内存占用 取值范围
    字节型 byte 1个字节 -128~127
    短整型 short 2个字节 -32768~32767
    整型 int(默认) 4个字节 -231次方~2的31次方-1
    长整型 long 8个字节 -2的63次方~2的63次方-1
    单精度浮点数 float 4个字节 1.4013E-45~3.4028E+38
    双精度浮点数 double(默认) 8个字节 4.9E-324~1.7977E+308
    字符型 char 2个字节 0-65535
    布尔类型 boolean 1个字节 true,false

    注意:
    long类型:建议数据后加L表示。
    float类型:建议数据后加F表示。

  • 自动转换规则

    byte、short、char‐‐>int‐‐>long‐‐>float‐‐>double

  • 强制转换

    将 1.5 赋值到 int 类型变量会发生什么?产生编译失败,肯定无法赋值。

    ​ int i = 1.5; // 错误

    强制类型转换:将 取值范围大的类型 强制转换成 取值范围小的类型 。 比较而言,自动转换是Java自动执行的,而强制转换需要我们自己手动行。 转换格式 :数据类型 变量名 = (数据类型)被转数据值;

    ​ int i = (int)1.5

    /*
    强制类型转换
      1. 特点:代码需要进行特殊的格式处理,不能自动完成。
      2. 格式:范围小的类型 范围小的变量名 = (范围小的类型) 原本范围大的数据;
    
    注意事项:
      1. 强制类型转换一般不推荐使用,因为有可能发生精度损失、数据溢出。
      2. byte/short/char这三种类型都可以发生数学运算,例如加法“+”.
      3. byte/short/char这三种类型在运算的时候,都会被首先提升成为int类型,然后再计算。
      4. boolean类型不能发生数据类型转换
    */
    public class Demo02DataType {
      public static void main(String[] args) {
          // 左边是int类型,右边是long类型,不一样
          // long --> int,不是从小到大
          // 不能发生自动类型转换!
          // 格式:范围小的类型 范围小的变量名 = (范围小的类型) 原本范围大的数据;
          int num = (int) 100L;
          System.out.println(num);
          
          // long强制转换成为int类型
          int num2 = (int) 6000000000L;
          System.out.println(num2); // 1705032704
          
          // double --> int,强制类型转换
          int num3 = (int) 3.99;
          System.out.println(num3); // 3,这并不是四舍五入,所有的小数位都会被舍弃掉
          
          char zifu1 = 'A'; // 这是一个字符型变量,里面是大写字母A
          System.out.println(zifu1 + 1); // 66,也就是大写字母A被当做65进行处理
          // 计算机的底层会用一个数字(二进制)来代表字符A,就是65
          // 一旦char类型进行了数学运算,那么字符就会按照一定的规则翻译成为一个数字
          
          byte num4 = 40; // 注意!右侧的数值大小不能超过左侧的类型范围
          byte num5 = 50;
          // byte + byte --> int + int --> int
          int result1 = num4 + num5;
          System.out.println(result1); // 90
          
          short num6 = 60;
          // byte + short --> int + int --> int
          // int强制转换为short:注意必须保证逻辑上真实大小本来就没有超过short范围,否则会发生数据溢出
          short result2 = (short) (num4 + num6);
          System.out.println(result2); // 100
      }
    }
    

运算符和变量、常量

  • +=符号的扩展

    public static void main(String[] args){
    short s = 1;
    s+=1;
    System.out.println(s);
    }
    

    分析: s += 1 逻辑上看作是 s = s + 1 计算结果被提升为int类型,再向short类型赋值时发生错误,因为不能将取值范围 大的类型赋值到取值范围小的类型。但是, s=s+1进行两次运算 , += 是一个运算符,只运算一次,并带有强制转换的特点, 也就是说 s += 1 就是 s = (short)(s + 1) ,因此程序没有问题编译通过,运行结果是2

  • 常量和变量的运算

    public static void main(String[] args){
    byte b1=1;
    byte b2=2;
    byte b3=1 + 2;
    byte b4=b1 + b2;
    System.out.println(b3);
    System.out.println(b4);
    }
    

    分析: b3 = 1 + 2 , 1 和 2 是常量,为固定不变的数据,在编译的时候(编译器javac),已经确定了 1+2 的结果并没 有超过byte类型的取值范围,可以赋值给变量 b3 ,因此 b3=1 + 2 是正确的。

    反之, b4 = b2 + b3 , b2 和 b3 是变量,变量的值是可能变化的,在编译的时候,编译器javac不确定b2+b3的结果是什 么,因此会将结果以int类型进行处理,所以int类型不能赋值给byte类型,因此编译失败

Idea 常用快捷键

快捷键 功能
Alt+Enter 导入包,自动修正代码
Ctrl+Y 删除光标所在行
Ctrl+D 复制光标所在行的内容,插入光标位置下面
Ctrl+Alt+L 格式化代码
Ctrl+/ 单行注释
Ctrl+Shift+/ 选中代码注释,多行注释,再按取消注释
Alt+Ins 自动生成代码,toString,get,set等方法
Alt+Shift+上下箭头 移动当前代码行

数组

  • 定义数组的三种方式

    int[] arr = new int[3];
    int[] arr = new int[]{1,2,3,4,5};
    int[] arr = {1,2,3,4,5};
    

    数组的长度属性arr.length

类和对象

数据类型 默认值
基本类型 整数(byte,short,int,long) 0
浮点数(float,double) 0.0
字符(char) '\u0000'
布尔(boolean) false
引用类型 数组,类,接口 null

常用API

  • Scanner类

    import java.util.Scanner;
    public class Demo01_Scanner {
      public static void main(String[] args) {
            //2. 创建键盘录入数据的对象
            Scanner sc = new Scanner(System.in);
            //3. 接收数据
            System.out.println("请录入一个整数:");
            int i = sc.nextInt();
            //4. 输出数据
            System.out.println("i:"+i);
        }
    }
    
  • 匿名对象

    创建对象时,只有创建对象的语句,却没有把对象地址值赋值给某个变量。虽然是创建对象的简化写法,但是应用 场景非常有限。

    匿名对象 :没有变量名的对象。

    一旦调用两次方法,就是创建了两个对象,造成浪费。一般可作为方法的参数和返回值

    new Scanner(System.in).nextInt();
    new Scanner(System.in).nextInt();
    
  • Random类

    public int nextInt(int n):返回一个伪随机数,范围在 0 (包括)和 指定值 n (不包括)之间的 int 值。

    //1. 导包
    import java.util.Random;
    public class Demo01_Random {
        public static void main(String[] args) {
            //2. 创建键盘录入数据的对象
            Random r = new Random();
            for(int i = 0; i < 3; i++){
                //3. 随机生成一个数据
                int number = r.nextInt(10);
                //4. 输出数据
                System.out.println("number:"+ number);
            }
        }
    }
    
  • ArrayList类

    java.util.ArrayList 是大小可变的数组的实现,存储在内的数据称为元素。此类提供一些方法来操作内部存储 的元素。 ArrayList 中可不断添加元素,其大小也自动增长。

    public class Test02StudentArrayList {
        public static void main(String[] args) {
            //创建学生数组
            ArrayList<String> list = new ArrayList<>();
            //创建学生对象
            String s1 = "曹操";
            String s2 = "刘备";
            String s3 = "孙权";
            //打印学生ArrayList集合
            System.out.println(list);
            //把学生对象作为元素添加到集合
            list.add(s1);
            list.add(s2);
            list.add(s3);
            //打印学生ArrayList集合
            System.out.println(list);
        }
    }
    

    常用方法:

    public boolean add(E e):将指定的元素添加到此集合的尾部。

    public E remove(int index) :移除此集合中指定位置上的元素。返回被删除的元素。

    public E get(int index):返回此集合中指定位置上的元素。返回获取的元素。

    public int size():返回此集合中的元素数。遍历集合时,可以控制索引范围,防止越界。

  • String类
    1.构造方法

public String() :初始化新创建的 String对象,以使其表示空字符序列。
public String(char[] value) :通过当前参数中的字符数组来构造新的String。
public String(byte[] bytes) :通过使用平台的默认字符集解码当前参数中的字节数组来构造新的String。
2.常用方法
判断功能:
public boolean equals (Object anObject) :将此字符串与指定对象进行比较。
public boolean equalsIgnoreCase (String anotherString) :将此字符串与指定对象进行比较,忽略大小写。
获取功能:
public int length () :返回此字符串的长度。
public String concat (String str) :将指定的字符串连接到该字符串的末尾。
public char charAt (int index) :返回指定索引处的 char值。
public int indexOf (String str):返回指定子字符串第一次出现在该字符串内的索引。
public String substring (int beginIndex) :返回一个子字符串,从beginIndex开始截取字符串到字符串结尾。
public String substring (int beginIndex, int endIndex) :返回一个子字符串,从beginIndex到endIndex截取字符串。含beginIndex,不含endIndex。
转换功能:
public char[] toCharArray ():将此字符串转换为新的字符数组。
public byte[] getBytes ():使用平台的默认字符集将该 String编码转换为新的字节数组。
public String replace (CharSequence target, CharSequence replacement):将与target匹配的字符串使用replacement字符串替换。
分割功能:
public String[] split(String regex):将此字符串按照给定的regex(规则)拆分为字符串数组。

public class String_Demo03 {
    public static void main(String[] args) {
        //创建字符串对象
        String s = "aa|bb|cc";
        String[] strArray = s.split("|"); // ["aa","bb","cc"]

        for(int x = 0; x < strArray.length; x++) {
            System.out.println(strArray[x]); // aa bb cc
        }
    }
}
  • static 关键字
    关于 static 关键字的使用,它可以用来修饰的成员变量和成员方法,被修饰的成员是属于类的,而不是单单是属于某个对象的。也就是说,既然属于类,就可以不靠创建对象来调用了。
    1.类变量:使用 static关键字修饰的成员变量。
    static 数据类型 变量名;
    static int numberID;
    2.类方法:即静态方法
    当 static 修饰成员方法时,该方法称为类方法 。静态方法在声明中有 static ,建议使用类名来调用,而不需要创建类的对象。
    静态方法调用的注意事项:
    静态方法可以直接访问类变量和静态方法。
    静态方法不能直接访问普通成员变量或成员方法。反之,成员方法可以直接访问类变量或静态方法。
    静态方法中,不能使用this关键字。
    3.静态代码块
    静态代码块:定义在成员位置,使用static修饰的代码块{ }。
    位置:类中方法外。
    执行:随着类的加载而执行且执行一次,优先于main方法和构造方法的执行。
    格式:
public class ClassName{
    static {
        // 执行语句
    }
}

static 关键字,可以修饰变量、方法和代码块。在使用的过程中,其主要目的还是想在不创建对象的情况下,去调用方法。

  • Object类
    public String toString():返回该对象的字符串表示。
    public boolean equals(Object obj):指示其他某个对象是否与此对象“相等”。

  • Objects类
    JDK7添加了一个Objects工具类,它提供了一些方法来操作对象,它由一些静态的实用方法组成,这些方法是null-save(空指针安全的)或null-tolerant(容忍空指针的),用于计算对象的hashcode、返回对象的字符串表示形式、比较两个对象。

在比较两个对象的时候,Object的equals方法容易抛出空指针异常,而Objects类中的equals方法就优化了这个问题。方法如下:

public static boolean equals(Object a, Object b):判断两个对象是否相等。

我们可以查看一下源码,学习一下:

public static boolean equals(Object a, Object b) {  
    return (a == b) || (a != null && a.equals(b));  
}
  • Date类
    public Date():分配Date对象并初始化此对象,以表示分配它的时间(精确到毫秒)。
    public Date(long date):分配Date对象并初始化此对象,以表示自从标准基准时间(称为“历元(epoch)”,即1970年1月1日00:00:00 GMT)以来的指定毫秒数。
import java.util.Date;

public class Demo01Date {
    public static void main(String[] args) {
        // 创建日期对象,把当前的时间
        System.out.println(new Date()); // Tue Jan 16 14:37:35 CST 2018
        // 创建日期对象,把当前的毫秒值转成日期对象
        System.out.println(new Date(0L)); // Thu Jan 01 08:00:00 CST 1970
    }
}

tips:在使用println方法时,会自动调用Date类中的toString方法。Date类对Object类中的toString方法进行了覆盖重写,所以结果为指定格式的字符串。
public long getTime() 把日期对象转换成对应的时间毫秒值。

  • DateFormat类
    格式化:按照指定的格式,从Date对象转换为String对象。
    解析:按照指定的格式,从String对象转换为Date对象。
    由于DateFormat为抽象类,不能直接使用,所以需要常用的子类java.text.SimpleDateFormat。这个类需要一个模式(格式)来指定格式化或解析的标准。构造方法为:
  • public SimpleDateFormat(String pattern):用给定的模式和默认语言环境的日期格式符号构造SimpleDateFormat。参数pattern是一个字符串,代表日期时间的自定义格式。
    常用的格式规则为:
标识字母(区分大小写) 含义
y
M
d
H
m
s
import java.text.DateFormat;
import java.text.SimpleDateFormat;

public class Demo02SimpleDateFormat {
    public static void main(String[] args) {
        // 对应的日期格式如:2018-01-16 15:06:38
        DateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    }    
}

DateFormat类的常用方法有:
public String format(Date date):将Date对象格式化为字符串。
public Date parse(String source):将字符串解析为Date对象。

  • System类
    java.lang.System类中提供了大量的静态方法,可以获取与系统相关的信息或系统级操作,在System类的API文档中,常用的方法有:
    public static long currentTimeMillis():返回以毫秒为单位的当前时间。
    public static void arraycopy(Object src, int srcPos, Object dest, int destPos, int length):将数组中指定的数据拷贝到另一个数组中。

实际上,currentTimeMillis方法就是 获取当前系统时间与1970年01月01日00:00点之间的毫秒差值

import java.util.Date;

public class SystemDemo {
    public static void main(String[] args) {
        //获取当前时间毫秒值
        System.out.println(System.currentTimeMillis()); // 1516090531144
    }
}

将src数组中前3个元素,复制到dest数组的前3个位置上复制元素前:src数组元素[1,2,3,4,5],dest数组元素[6,7,8,9,10]复制元素后:src数组元素[1,2,3,4,5],dest数组元素[1,2,3,9,10]

import java.util.Arrays;

public class Demo11SystemArrayCopy {
    public static void main(String[] args) {
        int[] src = new int[]{1,2,3,4,5};
        int[] dest = new int[]{6,7,8,9,10};
        System.arraycopy( src, 0, dest, 0, 3);
        /*代码运行后:两个数组中的元素发生了变化
         src数组元素[1,2,3,4,5]
         dest数组元素[1,2,3,9,10]
        */
    }
}
  • StringBuilder类
    StringBuilder是个字符串的缓冲区,即它是一个容器,容器中可以装很多字符串。并且能够对其中的字符串进行各种操作。
    根据StringBuilder的API文档,常用构造方法有2个:
    public StringBuilder():构造一个空的StringBuilder容器。
    public StringBuilder(String str):构造一个StringBuilder容器,并将字符串添加进去。

StringBuilder常用的方法有2个:
public StringBuilder append(...):添加任意类型数据的字符串形式,并返回当前对象自身。
public String toString():将当前StringBuilder对象转换为String对象。

append方法具有多种重载形式,可以接收任意类型的参数。任何数据作为参数都会将对应的字符串内容添加到StringBuilder中。例如:

public class Demo02StringBuilder {
    public static void main(String[] args) {
        //创建对象
        StringBuilder builder = new StringBuilder();
        //public StringBuilder append(任意类型)
        StringBuilder builder2 = builder.append("hello");
        //对比一下
        System.out.println("builder:"+builder);
        System.out.println("builder2:"+builder2);
        System.out.println(builder == builder2); //true
        // 可以添加 任何类型
        builder.append("hello");
        builder.append("world");
        builder.append(true);
        builder.append(100);
        // 在我们开发中,会遇到调用一个方法后,返回一个对象的情况。然后使用返回的对象继续调用方法。
        // 这种时候,我们就可以把代码现在一起,如append方法一样,代码如下
        //链式编程
        builder.append("hello").append("world").append(true).append(100);
        System.out.println("builder:"+builder);
    }
}

备注:StringBuilder已经覆盖重写了Object当中的toString方法。
通过toString方法,StringBuilder对象将会转换为不可变的String对象。如:

public class Demo16StringBuilder {
    public static void main(String[] args) {
        // 链式创建
        StringBuilder sb = new StringBuilder("Hello").append("World").append("Java");
        // 调用方法
        String str = sb.toString();
        System.out.println(str); // HelloWorldJava
    }
}
  • 包装类
    基本数值---->包装对象
    Integer i = new Integer(4);//使用构造函数函数
    Integer iii = Integer.valueOf(4);//使用包装类中的valueOf方法

包装对象---->基本数值
int num = i.intValue();

由于我们经常要做基本类型与包装类之间的转换,从Java 5(JDK 1.5)开始,基本类型与包装类的装箱、拆箱动作可以自动完成。例如:

Integer i = 4;//自动装箱。相当于Integer i = Integer.valueOf(4);
i = i + 5;//等号右边:将i对象转成基本数值(自动拆箱) i.intValue() + 5;
//加法运算完成后,再次装箱,把基本数值转成对象。

基本类型与字符串之间的转换:
基本类型直接与””相连接即可;如:34+""

String转换成对应的基本类型

除了Character类之外,其他所有包装类都具有parseXxx静态方法可以将字符串参数转换为对应的基本类型:
public static byte parseByte(String s):将字符串参数转换为对应的byte基本类型。
public static short parseShort(String s):将字符串参数转换为对应的short基本类型。
public static int parseInt(String s):将字符串参数转换为对应的int基本类型。
public static long parseLong(String s):将字符串参数转换为对应的long基本类型。
public static float parseFloat(String s):将字符串参数转换为对应的float基本类型。
public static double parseDouble(String s):将字符串参数转换为对应的double基本类型。
public static boolean parseBoolean(String s):将字符串参数转换为对应的boolean基本类型。

继承

  • 格式
class 父类 {
...
}
class 子类 extends 父类 {
...
}
  • 成员变量
    1.如果子类父类中出现不重名的成员变量,这时的访问是没有影响的。
    2.如果子类父类中出现重名的成员变量,这时的访问是有影响的。
    子父类中出现了同名的成员变量时,在子类中需要访问父类中非私有成员变量时,需要使用 super 关键字,修饰父类成员变量,类似于之前学过的 this 。
class Fu {
  // Fu中的成员变量。
  int num = 5;
} 
class Zi extends Fu {
  // Zi中的成员变量
  int num = 6;
  public void show() {
    // 访问父类中的num
    System.out.println("Fu num=" + super.num);
    // 访问子类中的num
    System.out.println("Zi num=" + this.num);
  }
}

class ExtendsDemo03 {
  public static void main(String[] args) {
  // 创建子类对象
  Zi z = new Zi();
    // 调用子类中的show方法
    z.show();
  }
} 
演示结果:
Fu num = 5
Zi num = 6

小贴士:Fu 类中的成员变量是非私有的,子类中可以直接访问。若Fu 类中的成员变量私有了,子类是不能直接访问的。通常编码时,我们遵循封装的原则,使用private修饰成员变量,那么如何访问父类的私有成员变量呢?对!可以在父类中提供公共的getXxx方法和setXxx方法。

  • 成员方法
    1.如果子类父类中出现不重名的成员方法,这时的调用是没有影响的。对象调用方法时,会先在子类中查找有没有对应的方法,若子类中存在就会执行子类中的方法,若子类中不存在就会执行父类中相应的方法。
    2.如果子类父类中出现重名的成员方法,这时的访问是一种特殊情况,叫做方法重写 (Override)。
    小贴士:重写时,用到super.父类成员方法,表示调用父类的成员方法

注意事项
子类方法覆盖父类方法,必须要保证权限大于等于父类权限。
子类方法覆盖父类方法,返回值类型、函数名和参数列表都要一模一样。

  • *构造方法
  1. 构造方法的名字是与类名一致的。所以子类是无法继承父类构造方法的。
  2. 构造方法的作用是初始化成员变量的。所以子类的初始化过程中,必须先执行父类的初始化动作。子类的构造方法中默认有一个 super() ,表示调用父类的构造方法,父类成员变量初始化后,才可以给子类使用。

this(...) ‐‐ 本类的构造方法
super(...) ‐‐ 父类的构造方法

子类的每个构造方法中均有默认的super(),调用父类的空参构造。手动调用父类构造会覆盖默认的super()。super() 和 this() 都必须是在构造方法的第一行,所以不能同时出现。

抽象类

定义:
抽象方法 : 没有方法体的方法。
抽象类:包含抽象方法的类。

  • 抽象方法
    使用 abstract 关键字修饰方法,该方法就成了抽象方法,抽象方法只包含一个方法名,而没有方法体。
修饰符 abstract 返回值类型 方法名 (参数列表);
public abstract void run();
  • 抽象类
    如果一个类包含抽象方法,那么该类必须是抽象类。
abstract class 类名字 {
} 
public abstract class Animal {
  public abstract void run();
}

继承抽象类的子类必须重写父类所有的抽象方法。否则,该子类也必须声明为抽象类。最终,必须有子类实现该父类的抽象方法,否则,从最初的父类到最终的子类都不能创建对象,失去意义。

注意事项

  1. 抽象类不能创建对象,如果创建,编译无法通过而报错。只能创建其非抽象子类的对象。
    理解:假设创建了抽象类的对象,调用抽象的方法,而抽象方法没有具体的方法体,没有意义。
  2. 抽象类中,可以有构造方法,是供子类创建对象时,初始化父类成员使用的。
    理解:子类的构造方法中,有默认的super(),需要访问父类构造方法。
  3. 抽象类中,不一定包含抽象方法,但是有抽象方法的类必定是抽象类
    理解:未包含抽象方法的抽象类,目的就是不想让调用者创建该类对象,通常用于某些特殊的类结构设计。
  4. 抽象类的子类,必须重写抽象父类中所有的抽象方法,否则,编译无法通过而报错。除非该子类也是抽象类。
    理解:假设不重写所有抽象方法,则类中可能包含抽象方法。那么创建对象后,调用抽象的方法,没有意义。

接口

接口,是Java语言中一种引用类型,是方法的集合,如果说类的内部封装了成员变量、构造方法和成员方法,那么接口的内部主要就是封装了方法,包含抽象方法(JDK 7及以前),默认方法和静态方法(JDK 8),私有方法(JDK 9)。
接口的定义,它与定义类方式相似,但是使用 interface 关键字。它也会被编译成.class文件,但一定要明确它并不是类,而是另外一种引用数据类型。引用数据类型:数组,类,接口。
接口的使用,它不能创建对象,但是可以被实现( implements ,类似于被继承)。一个实现接口的类(可以看做是接口的子类),需要实现接口中所有的抽象方法,创建该类对象,就可以调用方法了,否则它必须是一个抽象类。
定义格式

public interface 接口名称 {
  // 抽象方法
  // 默认方法
  // 静态方法
  // 私有方法
}

/*
含有抽象方法
抽象方法:使用 abstract 关键字修饰,可以省略,没有方法体。该方法供子类实现使用。
*/
public interface InterFaceName {
  public abstract void method(); //使用 abstract 关键字修饰,可以省略,没有方法体。
}

/*
含有默认方法和静态方法
默认方法:使用 default 修饰,不可省略,供子类调用或者子类重写。
静态方法:使用 static 修饰,供接口直接调用。静态与.class 文件相关,只能使用接口名调用,不可以通过实现类的类名或者实现类的对象调用
*/
public interface InterFaceName {
  public default void method() {
    // 执行语句
  }
 public static void method2() {
  // 执行语句
 }
}

/*含有私有方法和私有静态方法
私有方法:使用 private 修饰,供接口中的默认方法或者静态方法调用。*/
public interface InterFaceName {
  private void method() {
    // 执行语句
  }
}
  • 基本实现
    类与接口的关系为实现关系,即类实现接口,该类可以称为接口的实现类,也可以称为接口的子类。实现的动作类似继承,格式相仿,只是关键字不同,实现使用 implements 关键字。
    非抽象子类实现接口:
  1. 必须重写接口中所有抽象方法。
  2. 继承了接口的默认方法,即可以直接调用,也可以重写。但是只能通过实现类的对象来调用
    实现格式:
class 类名 implements 接口名 {
  // 重写接口中抽象方法【必须】
  // 重写接口中默认方法【可选】
}

Tips:

类的静态变量和静态方法能否被子类继承?
结论:java中静态属性和静态方法可以被继承,但是没有被重写(overwrite)而是被隐藏.
原因:
1). 静态方法和属性是属于类的,调用的时候直接通过类名.方法名完成对,不需要继承机制及可以调用。如果子类里面定义了静态方法和属性,那么这时候父类的静态方法或属性称之为"隐藏"。如果你想要调用父类的静态方法和属性,直接通过父类名.方法或变量名完成,至于是否继承一说,子类是有继承静态方法和属性,但是跟实例方法和属性不太一样,存在"隐藏"的这种情况。
2). 多态之所以能够实现依赖于继承、接口和重写、重载(继承和重写最为关键)。有了继承和重写就可以实现父类的引用指向不同子类的对象。重写的功能是:"重写"后子类的优先级要高于父类的优先级,但是“隐藏”是没有这个优先级之分的。
3). 静态属性、静态方法和非静态的属性都可以被继承和隐藏而不能被重写,因此不能实现多态,不能实现父类的引用可以指向不同子类的对象。非静态方法可以被继承和重写,因此可以实现多态。

package com.study.test;
 
public class A { //父类
    public static String staticStr = "A静态属性";
    public String nonStaticStr = "A非静态属性";
    public static void staticMethod(){
        System.out.println("A静态方法");
    }
    public void nonStaticMethod(){
        System.out.println("A非静态方法");
    }
}

package com.study.test;
 
public class B extends A{//子类B
    public static String staticStr = "B改写后的静态属性";
    public String nonStaticStr = "B改写后的非静态属性";
    public static void staticMethod(){
        System.out.println("B改写后的静态方法");
    }
}

package com.study.test;
 
public class C extends A{//子类C继承A中的所有属性和方法
 
}

package com.study.test;
 
public class StaticExtendsTest {
 
    public static void main(String[] args) {
        C c = new C();
        System.out.println(c.nonStaticStr);
        System.out.println(c.staticStr);
        c.staticMethod();//输出的结果都是父类中的非静态属性、静态属性和静态方法,推出静态属性和静态方法可以被继承
        
        System.out.println("-------------------------------");
        
        A c1 = new C();
        System.out.println(c1.nonStaticStr);
        System.out.println(c1.staticStr);
        c1.staticMethod();//结果同上,输出的结果都是父类中的非静态属性、静态属性和静态方法,推出静态属性和静态方法可以被继承
    
        System.out.println("-------------------------------");
        B b = new B();
        System.out.println(b.nonStaticStr);
        System.out.println(b.staticStr);
        b.staticMethod();
        
        System.out.println("-------------------------------");
        A b1 = new B();
        System.out.println(b1.nonStaticStr);
        System.out.println(b1.staticStr);
        b1.staticMethod();//结果都是父类的静态方法,说明静态方法不可以被重写,不能实现多态
    }
 
}

测试结果如下:

A非静态属性
A静态属性
A静态方法
-------------------------------
A非静态属性
A静态属性
A静态方法
-------------------------------
B改写后的非静态属性
B改写后的静态属性
B改写后的静态方法
-------------------------------
A非静态属性
A静态属性
A静态方法
  • 接口的多实现
    之前学过,在继承体系中,一个类只能继承一个父类。而对于接口而言,一个类是可以实现多个接口的,这叫做接口的多实现。并且,一个类能继承一个父类,同时实现多个接口。
class 类名 [extends 父类名] implements 接口名1,接口名2,接口名3... {
 // 重写接口中抽象方法【必须】
 // 重写接口中默认方法【不重名时可选】
}

接口中,有多个抽象方法时,实现类必须重写所有抽象方法。如果抽象方法有重名的,只需要重写一次.
接口中,有多个默认方法时,实现类都可继承使用。如果默认方法有重名的,必须重写一次
接口中,存在同名的静态方法并不会冲突,原因是只能通过各自接口名访问静态方法。

  • 接口的多继承
    一个接口能继承另一个或者多个接口,这和类之间的继承比较相似。接口的继承使用 extends 关键字,子接口继承父接口的方法。如果父接口中的默认方法有重名的,那么子接口需要重写一次

小贴士:
子接口重写默认方法时,default关键字可以保留。
子类重写默认方法时,default关键字不可以保留。

接口中,无法定义成员变量,但是可以定义常量,其值不可以改变,默认使用public static final修饰。
接口中,没有构造方法,不能创建对象。
接口中,没有静态代码块。

多态

多态是继封装、继承之后,面向对象的第三大特性

父类类型 变量名 = new 子类对象;
变量名.方法名();
Fu f = new Zi();
f.method();  //多态规定,执行的是子类重写的方法
  • 多态的转型分为向上转型与向下转型两种:
    向上转型
    向上转型:多态本身是子类类型向父类类型向上转换的过程,这个过程是默认的。
    当父类引用指向一个子类对象时,便是向上转型。
    使用格式:
父类类型 变量名 = new 子类类型();
如:Animal a = new Cat();

向下转型
向下转型:父类类型向子类类型向下转换的过程,这个过程是强制的。
使用格式:

子类类型 变量名 = (子类类型) 父类变量名;
如:Cat c =(Cat) a;

当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误。也就是说,不能调用子类拥有,而父类没有的方法。编译都错误,更别说运行了。这也是多态给我们带来的一点"小麻烦"。所以,想要调用子类特有的方法,必须做向下转型。
转型的异常
转型的过程中,一不小心就会遇到这样的问题,请看如下代码:

public class Test {
  public static void main(String[] args) {
    // 向上转型
    Animal a = new Cat();
    a.eat(); // 调用的是 Cat 的 eat
    // 向下转型
    Dog d = (Dog)a;
    d.watchHouse(); // 调用的是 Dog 的 watchHouse 【运行报错】
  }
}

为了避免ClassCastException的发生,Java提供了 instanceof 关键字,给引用变量做类型的校验,格式如下:
变量名 instanceof 数据类型
如果变量属于该数据类型,返回true。
如果变量不属于该数据类型,返回false。

public class Test {
public static void main(String[] args) {
// 向上转型
Animal a = new Cat();
a.eat(); // 调用的是 Cat 的 eat
// 向下转型
if (a instanceof Cat){
Cat c = (Cat)a;
c.catchMouse(); // 调用的是 Cat 的 catchMouse
} else if (a instanceof Dog){
Dog d = (Dog)a;
d.watchHouse(); // 调用的是 Dog 的 watchHouse
}
}
}

final关键字

final: 不可改变。可以用于修饰类、方法和变量。
类:被修饰的类,不能被继承。
方法:被修饰的方法,不能被重写。
变量:被修饰的变量,不能被重新赋值。

  • 修饰类
    查询API发现像 public final class String 、 public final class Math 、 public final class Scanner等,很多我们学习过的类,都是被final修饰的,目的就是供我们使用,而不让我们所以改变其内容。
  • 修饰方法
修饰符 final 返回值类型 方法名(参数列表){
  //方法体
}

重写被 final 修饰的方法,编译时就会报错。

  • 修饰变量
    1、局部变量——基本类型
    基本类型的局部变量,被final修饰后,只能赋值一次,不能再更改。
public class FinalDemo1 {
public static void main(String[] args) {
// 声明变量,使用final修饰
final int a;
// 第一次赋值
a = 10;
// 第二次赋值
a = 20; // 报错,不可重新赋值
// 声明变量,直接赋值,使用final修饰
final int b = 10;
// 第二次赋值
b = 20; // 报错,不可重新赋值
}
}
  1. 局部变量——引用类型
    引用类型的局部变量,被final修饰后,只能指向一个对象,地址不能再更改。但是不影响对象内部的成员变量值的修改
public class FinalDemo2 {
public static void main(String[] args) {
// 创建 User 对象
final User u = new User();
// 创建 另一个 User对象
u = new User(); // 报错,指向了新的对象,地址值改变。
// 调用setName方法
u.setName("张三"); // 可以修改
}
}
  1. 成员变量
    成员变量涉及到初始化的问题,初始化方式有两种,只能二选一
    被final修饰的常量名称,一般都有书写规范,所有字母都大写。
    显示初始化:
public class User {
  final String USERNAME = "张三";
  private int age;
}

构造方法初始化:

public class User {
  final String USERNAME ;
  private int age;
  public User(String username, int age) {
    this.USERNAME = username;
   this.age = age;
  }
}

权限修饰符

public protected default(空的) private
同一类中
同一包中(子类与无关类)
不同包的子类
不同包中的无关类

编写代码时,如果没有特殊的考虑,建议这样使用权限:
成员变量使用 private ,隐藏细节。
构造方法使用 public ,方便创建对象。
成员方法使用 public ,方便调用方法。
小贴士:不加权限修饰符,其访问能力与default修饰符相同

内部类

成员内部类
成员内部类 :定义在类中方法外的类。

class 外部类 {
  class 内部类{
  }
}

访问特点
内部类可以直接访问外部类的成员,包括私有成员。
外部类要访问内部类的成员,必须要建立内部类的对象。
创建内部类对象格式:
外部类名.内部类名 对象名 = new 外部类型().new 内部类型();

public class Person {
private boolean live = true;
class Heart {
public void jump() {
// 直接访问外部类成员
if (live) {
System.out.println("心脏在跳动");
} else {
System.out.println("心脏不跳了");
}
}
}
 public boolean isLive() {
return live;
} 
public void setLive(boolean live) {
this.live = live;
}
}

public class InnerDemo {
public static void main(String[] args) {
// 创建外部类对象
Person p = new Person();
// 创建内部类对象
Heart heart = p.new Heart();
// 调用内部类方法
heart.jump();
// 调用外部类方法
p.setLive(false);
// 调用内部类方法
heart.jump();
}
} 
输出结果:
心脏在跳动
心脏不跳了
  • 匿名内部类对象
    匿名内部类 :是内部类的简化写法。它的本质是一个 带具体实现的 父类或者父接口的 匿名的 子类对象。
    格式:
new 父类名或者接口名(){
  // 方法重写
  @Override
  public void method() {
    // 执行语句
  }
};

public abstract class FlyAble{
  public abstract void fly();
}

public class InnerDemo {
  public static void main(String[] args) {
  /*
  1.等号右边:是匿名内部类,定义并创建该接口的子类对象
  2.等号左边:是多态赋值,接口类型引用指向子类对象
  */
  FlyAble f = new FlyAble(){
    public void fly() {
      System.out.println("我飞了~~~");
    }
  };

  //调用 fly方法,执行重写后的方法
  f.fly();
  }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,053评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,527评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,779评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,685评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,699评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,609评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,989评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,654评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,890评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,634评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,716评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,394评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,976评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,950评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,191评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,849评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,458评论 2 342

推荐阅读更多精彩内容