Java - 基础语法一

Java - 基础语法一

① 基础语法

  • JVM(Java Virtual Machine): Java 虚拟机,简称 JVM,是运行所有 Java 程序的假想计算机,是 Java 程序的运行环境,是 Java 最具吸引力的特性之一。我们编写的 Java 程序,都运行在 JVM 之上。
  • JRE(Java Runtime Environment):Java 程序的运行环境,包含 JVM 和运行时所需要的核心类库。
  • JDK(Java Development Environment):Java 程序开发工具包,包含 JRE 和开发人员使用的工具。

方法

格式:

修饰符 返回值类型 方法名(参数列表)
{
    // 代码
    return 结果;// 如果返回值类型不为 void
}

方法重载:多个方法的名称一样,但是参数列表不一样

方法重载与以下因素有关:

  • 参数个数不同
  • 参数类型不同
  • 参数的多类型顺序不同

方法重载与以下因素无关:

  • 与参数的名称无关
  • 与方法的返回值类型无关

数组:一种容器,可以同时存放多个数据源

数组特点:

  1. 数组是一种引用数据类型
  2. 数组当中的多个数据,类型必须统一
  3. 数组的长度在程序运行期间不可改变

动态初始化:在创建数组的时候,直接指定数组当中的数据元素个数

格式:数据类型[] 数组名称 = new 数据类型[数组长度]

静态初始化:在创建数组的时候,不直接指定数据个数多少,而是直接将具体的数据内容进行指定

标准格式:数据类型[] 数组名称 = new 数据类型[] {元素 1、元素 2、...}
省略格式:数据类型[] 数组名称 = {元素 1、元素 2、...}

注意事项

  1. 静态初始化没有直接指定长度,但是仍然会自动推算得到长度 int[] arrayA = {10, 20, 30};
  2. 静态初始化标准格式可以拆分成两个步骤 int[] arrayB; arrayB = new int[5];
  3. 动态初始化也可以拆分成两个步骤 int arrayC; arrayC = new int [] {1, 2, 3 };
  4. 静态初始化一旦使用省略格式,就不能拆分成为两个步骤了

Java 的内存需要划分成为 5 个部分:

  1. 栈(Srack):存放的都是方法中的局部变量,方法的运行一定在栈中
    1. 局部变量:方法的参数,或者是方法{} 内部的变量。
    2. 作用域:一旦超出作用域,立刻从栈内存当中消失。
  2. 堆(Heap):凡是 new 出来的东西,都在堆当中
    1. 堆内存里面的东西都有一个地址值:16 进制
    2. 堆内存里面的数据,都有默认值
  3. 方法区(Method Area):存储 .class 相关信息,包含方法的信息。
  4. 本地方法栈(Native Method Stack):与操作系统相关
  5. 寄存器(pc Register):与 CPU 相关

② 面向对象

局部变量和成员变量

  • 定义的位置不同
    • 局部变量:在方法内部
    • 成员变量:在方法外部,直接写在类中
  • 作用范围不同
    • 局部变量:只有在方法中才可以使用,出了方法就不能用
    • 成员变量:整个类全都可以通用
  • 默认值不同
    • 局部变量:没有默认值,如果想使用,必须手动进行赋值
    • 成员变量:如果没有赋值,会有默认值,规则和数组一样
  • 内存位置不一样
    • 局部变量:位于栈内存
    • 成员变量:位于堆内存
  • 生命周期不一样
    • 局部变量:随方法进栈而诞生,随着方法出栈而消失
    • 成员变量:随着对象创建而诞生,随着对象被垃圾回收而消失

面向对象三大特性:封装、继承、多态

封装性在 Java 体现:

  • 方法就是封装
  • Private 关键字也是封装

对于基本数据类型当中的 boolean 值,Getter 方法一定要写成 isXxx 的形式,而 setXxx 规则不变

构造方法

构造方法是专门用来创建对象的方法,当我们通过关键字 new 来创建对象时,其实就是在调用构造方法

格式:

public 类名称(参数类型 参数名称) 
{
    方法体
}

构造方法使用注意事项:

  • 构造方法的名称必须和所在的类名称完全一样,就连大小写也要一样
  • 构造方法不要写返回值类型,连 Void 都不写
  • 构造方法不能 return 一个具体返回值
  • 如果没有编写任何构造方法,编译器会默认生成一个构造方法,无参数、无方法体
  • 一旦编写了至少一个构造方法,编译器便不再自动生成

标准的类

  1. 所有的成员变量都要用 private 关键字修饰
  2. 为每一个成员变量编写一对儿 Getter/Setter 方法
  3. 编写一个无参数的构造方法
  4. 编写一个全参数的构造方法

③ 常用 API

Random

用于生成随机数
public int nextInt(int n) 返回一个 [0,n) 之间的随机数。

ArrayList

数组的长度不可以发生变化,ArrayList 集合的长度是可以随意变化的。
如果希望向 ArrayList 当中存储基本类型数据,必须使用基本类型对应的“包装类”

基本类型 包装类(引用类型)
byte Byte
short Short
int Integer
long Long
float Float
double Double
char Character
boolean Boolean

String

程序中所有双引号字符串,都是 String 类的对象

特点

  • 字符串的内容永不改变
  • 正是因为字符串不可改变,所以字符串是可以共享使用的
  • 字符串效果上相当于是 char[] 字符数组,底层原理是 byte[] 字节数组

字符串常量池,在堆中开辟的一块空间。 使用 "" 直接创建的字符串都存放在字符串常量池中。

  1. 对于引用类型来说,“==” 进行的是地址值的比较,基本数据类型是对值的比较;
  2. 双引号直接创建的字符串在常量池中,new 的不在池中。

如果需要对字符串的内容进行比较,可以使用如下方法:

public boolean equals(Object obj); // 严格区分大小写
public boolean equalsIgnoreCase(Object obj); // 忽略大小写

split 方法的参数其实是一个正则表达式,如果要按照英文 . 进行切分,必须写 "\\." (两个反斜杠)

Static 关键字

用来修饰成员变量和成员方法,被修饰的成员属于类的,而不是单单是属于某个对象的。

  • static 修饰成员变量,该变量称为类变量,该类的每个对象都共享同一个类变量的值。
    格式:
static 数据类型 变量名;
  • static 修饰成员方法时,该方法称为类方法。

静态代码块:定义在成员位置,使用 static 修饰的代码块 {}

  • 位置:类中方法之外
  • 执行:随着类的加载而执行且只执行一次,优先于 main 方法和构造方法的执行。
  • 作用:类类变量进行初始化赋值。

格式:

public class className 
{
    static {
        /// 执行语句    
    }
}

Arrays

java.util.Arrays 是一个与数组相关的工具类,里面提供了大量的静态方法,用来实现数组常见操作。
toString(数组) 将参数数组变成字符串
sort(数组) 按照默认升序对数组的元素进行排序:注意 如果数组内是自定义类型,那么这个自定义类需要有 Comparable 或者 Comparator 接口的支持。

Math

abs(double num) 绝对值
ceil(double num) 向上取整
floor(double num) 向下取整
round(double num) 四舍五入
Math.PI 圆周率

④ 继承

继承

子类继承父类的属性和行为,使得子类对象具有父类相同的属性、相同的行为,子类可以直接访问父类中的非私有的属性和行为。
优点:
1、提高代码的复用性
2、类与类之间产生了关系,是多态的前提。

子父类中出现了同名的成员变量时,在子类中需要访问父类中非私有变量时,需要使用 super 关键字修饰父类成员变量。

如果子父类中出现了同名的成员方法,这是的访问是一种特殊情况-称之为方法重写

Java 中指支持单继承,不支持多继承。

抽象类

父类中的方法,被他的子类们重写,子类各自的实现都不尽相同,那么父类的方法声明和方法主体,只有声明还有意义,而方法主体则没有存在的意义了,我们把没有方法主体的方法称为抽象方法。包含抽象方法的类就是抽象类。

抽象方法

抽象方法,方法前加 abstract 关键字,去掉关键字,直接分号结束。
定义格式:

修饰符 abstract 返回值类型 方法名(参数列表);

抽象类

抽象类:抽象方法所在的类,必须是抽象类才行,在 class 之前写上 abstract 即可。抽象类可以定义正常方法。
定义格式:

abstract class 类名字
{
    ///xxx
}

注意:

  • 抽象类不能实例化,必须用子类继承抽象父类,子类必须覆盖重写抽象类的所有抽象方法。
  • 抽象类中,可以有构造方法,是供子类创建对象时,初始化父类成员使用的。
  • 抽象类中,不一定包含抽象方法,但是有抽象方法的类必定是抽象类。
  • 抽象类的子类,必须重写抽象父类中所有的抽象方法,否则,编译无法通过而报错,除非子类也是抽象类。

⑤ 接口

接口是 Java 中的一种引用类型,是方法的集合,如果类的内部封装了成员变量,构造方法和成员方法,那么接口的内部主要就是封装了方法。接口不是类,是一种引用数据类型,其他的引用数据类型还包括:数组、类。
接口可包含五部分:常量、抽象方法、默认方法、静态方法、私有方法
定义格式:

public interface 接口名称
{
    // 抽象方法,继承类必须全部实现
    // 默认方法,default 修饰符修饰,可以继承可以重写,必须通过实现类来调用
    // 静态方法,只能通过接口名调用,不可通过实现类名或者实现类对象调用
    // 私有方法,私有成员方法只有默认方法可以调用、私有静态方法只有默认方法和静态方法可以调用
}

接口使用步骤:

  1. 接口不能直接使用,必须有一个“实现类”来“实现”该接口;格式:public class 实现类名称 implement 接口名称
  2. 接口的实现类必须覆盖重写接口中所有的抽象方法。
  3. 创建实现类的对象,进行使用

注意事项:

  1. 如果实现类并没有覆盖重写接口的所有抽象方法,那么这个实现类自己就必须是抽象类。
  2. 接口没有静态代码块或者构造方法
  3. 一个类的直接父类是唯一的,但是一个类可以用时实现多个接口。格式:public class 类名称 implements 接口 A, 接口 B {}
  4. 多个接口存在相同名称抽象方法,实现类只需覆盖重写一次即可。
  5. 如果实现类没有覆盖重写所有接口中定义的所有抽象方法,那么实现类就必须是一个抽象类。
  6. 如果多个接口中存在相同名称的默认方法,那么实现类一定要对冲突的默认方法进行覆盖重写。
  7. 一个类如果直接父类中的方法和接口当中的默认方法产生了冲突,优先用父类中的方法。(Java 继承优先级高于接口)

接口中的方法默认是抽象方法

格式: public abstract 返回值类型 方法名称(参数列表);
实现类必须覆盖重写接口所有抽象方法,除非实现类是抽象类。

接口中的默认方法

接口中的默认方法可以解决接口升级的问题。
格式:public default 返回值类型 方法名称(参数列表);

接口中静态方法

不能用接口实现类的对象来调用接口中的静态方法,应该直接使用接口名称直接调用静态方法。
格式:public static 返回值类型 方法名称(参数列表);

接口中的私有方法

抽取公共方法用来解决多个默认方法之间重复代码的问题,但这个共有方法不应该让实现类使用,应该是私有化的。
格式:private default 返回值类型 方法名称(参数列表);private static 返回值类型 方法名称(参数列表);

接口中的“成员变量” 即 “常量”

接口中可以定义“成员变量”,但必须使用 public static final 三个关键字进行修饰(可省略)。从效果上看,这其实就是接口的“常量”。一旦使用 final 关键字,说明不可变
格式:public static final 数据类型 名称 = 数据值;
注意事项:

  1. 接口中的常量,可以省略 public static final 关键字。含义不变。
  2. 接口中的常量,必须进行赋值,不能不赋值。
  3. 建议常量名称大写。多个单词用 _ 分割。

接口中的多继承

  • 类与类之间是单继承的,直接父类只有一个
  • 类与接口之间是多继承的,一个类可以实现多个接口
  • 接口与接口之间是多继承的
    • 多个父接口中的抽象方法可以重复
    • 多个父接口中的默认方法如果重复,必须在子接口进行覆盖重写,并带 default 关键字。

接口其他特性

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

⑥ 多态

对象具有多个形态
代码中体现多态性,父类引用指向子类对象。格式:父类名称 对象名 = new 子类名称();接口名称 对象名 = new 实现类名称();

多态访问成员变量的两种方式:

  1. 直接通过对象名称访问成员变量:看等号左边是谁,优先用谁,没有则向上找。(成员变量无法进行覆盖重写)
  2. 间接通过成员方法访问成员变量:看该方法属于谁,优先用谁,没有则向上找。

多态访问成员方法:
看 new 的是谁,就优先用谁,没有则向上找。

口诀:
多态访问成员变量:编译看左边,运行也看左边
多态访问成员方法:编译看左边,运行看右边。

多态的好处:无论右边 new 的是哪个子类对象,左边声明的类和调用的方法可以保持一致。

对象的向上转型

其实就是多态的写法:父类名称 对象名 = new 子类名称();
含义:右侧创建一个子类对象,把它当做父类看待使用。
注意事项:向上转型一定是安全的。类似于基本数据类型的类型转换 (float -> double)
弊端:一旦向上转型为父类,那么就无法调用子类原本特有的内容。(解决方案-使用向下转型)

对象的向下转型

其实是一个还原的动作。格式:子类名称 对象名 = (子类名称)父类对象;
含义:将父类对象,还原为本来的子类对象
注意事项:
必须保证对象创建的时候就是向下转型的类型。

instanceof 关键字

返回一个 boolean 值,判断前面的对象能不能当做后面类型的实例。
格式:对象名 instanceof 类名

⑦ 其他

final 关键字

用于修饰不可改变的内容

final 用法

  1. 修饰类:不能有任何子类,而且一个类如果是 final 的,那么其中所有的成员方法都无法对其进行覆盖重写
  2. 修饰方法:这个方法不能被覆盖重写,对于类和方法而言,abstract 关键字和 final 关键字不能同时使用,因为矛盾。
  3. 修饰局部变量:这个变量只能被赋值一次,不能再次被修改。“一次赋值,终生不变”。对于基本数据类型,不可变值得是变量中的数据不可变;对于引用类型,不可变指的是变量中的地址值不可变。
  4. 修饰成员变量:这个变量只能而且必须被赋值一次,不能再次被修改。而且不再有默认值。
    1. 由于成员变量具有默认值,所以用了 final 之后必须手动赋值,不会再有默认值。
    2. 对于 final 的成员变量,要么使用直接赋值,要么使用构造方法赋值,两者取其一。
    3. 如果使用构造方法对 final 修饰的成员变量赋值,必须保证类中所有重载的构造方法,都最终会对 final 的成员变量进行赋值。
  5. final 修饰的常量名称,一般有书写规范,所有字母均大写。

Java 中的四种权限修饰符

public > protected (专门给不同包子类用的) > (default) > private

public protected (default) private
同一个类(我自己) YES YES YES YES
用一个包(我邻居) YES YES YES NO
不同包子类(我儿子) YES YES NO NO
不同包非子类(陌生人) YES NO NO NO

内部类

分类:

  1. 成员内部类
  2. 局部内部类(包含匿名内部类)

成员内部类

格式:

修饰符 class 外部类名称 
{
    修饰符 class 内部类名称
    {
        xxx
    }
}

内部类可以直接访问外部类的成员,包括私有成员。
外部类要访问内部类的成员,必须建立内部类的对象。
即 : 内用外,随意访问。外用内需要借助内部类对象。

如何使用成员内部类

  1. 间接方式:在外部类的方法中,使用内部类;然后 main 只是调用外部类的方法。
  2. 直接方式:定义格式:外部类名称.内部类名称 对象名 = new 外部类名称().new 内部类名称();

内部类使用外部类的成员变量

如果出现重名现象,使用格式:外部类名称.this.外部类成员变量名
实例:

public class Outer
{
    int num = 10;
     public class Inner
     {
        int num = 20;
        public void methodInner()
        {
           int num = 30;
           System.out.println(num); // 局部变量,就近原则
           System.out.println(this.num); // 内部类的成员变量
           System.out.println(Outer.this.num); // 外部类的成员变量
        }
     }
}

局部内部类

如果一个类定义在一个方法内部,那么就是局部内部类。
“局部”:只有当前所属的方法才能使用它,出了这个方法外面就不能用了。

局部内部类如果要访问所在方法的局部变量,那么这个局部变量必须是【有效 final 的】即 用 final 声明的变量或者事实上只赋值一次的变量。
原因:new 出来的对象在堆内存中,局部变量跟着方法走,在栈内存中。方法运行结束立刻出栈,局部变量立即消失。但是 new 出来的对象会在堆中持续存在,直到垃圾回收消失。

定义类的权限修饰符

  • 外部类:public 或 (default)
  • 成员内部类:都可以
  • 局部内部类:什么都不能写

匿名内部类

如果接口的实现类(或者是父类的子类)只需要使用唯一的一次,那么这种情况下就可以省略掉该类的定义,而改为使用匿名内部类

定义格式:

接口名称 对象名 = new 接口名称()
{
    /// 覆盖重写接口中所有的抽象方法。
}

对格式 "new 接口名称(){...}" 进行解析:

  1. new 代表创建对象的动作
  2. 接口名称就是匿名内部类需要实现哪个接口
  3. {...} 这才是匿名内部类的内容

注意事项:

  1. 匿名内部类,在【创建对象】的时候只能使用唯一一次,如果希望多次创建对象,那么就必须使用单独定义的实现类。
  2. 匿名对象,在【调用方法】的时候只能调用唯一一次,如果希望同一个对象调用多次方法,那么必须给对象起个名字。
  3. 匿名内部类是省略了【实现类/子类名称】,但是匿名对象时省略了【对象名称】,匿名内部类和匿名对象不是一回事!!

interface 作为成员变量

使用接口作为成员变量以便随时更换实现方式,这种设计更为灵活,增强了程序的扩展性。
接口作为成员变量时,对他进行复制的操作,实际上是赋给他接口的一个子类实现对象。

接口作为参数数,传递它的子类对象;
接口作为返回值类型是,也是返回它的子类对象。