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
}
方法重载:多个方法的名称一样,但是参数列表不一样
方法重载与以下因素有关:
- 参数个数不同
- 参数类型不同
- 参数的多类型顺序不同
方法重载与以下因素无关:
- 与参数的名称无关
- 与方法的返回值类型无关
数组:一种容器,可以同时存放多个数据源
数组特点:
- 数组是一种引用数据类型
- 数组当中的多个数据,类型必须统一
- 数组的长度在程序运行期间不可改变
动态初始化:在创建数组的时候,直接指定数组当中的数据元素个数
格式:数据类型[] 数组名称 = new 数据类型[数组长度]
静态初始化:在创建数组的时候,不直接指定数据个数多少,而是直接将具体的数据内容进行指定
标准格式:数据类型[] 数组名称 = new 数据类型[] {元素 1、元素 2、...}
省略格式:数据类型[] 数组名称 = {元素 1、元素 2、...}
注意事项
- 静态初始化没有直接指定长度,但是仍然会自动推算得到长度
int[] arrayA = {10, 20, 30};
- 静态初始化标准格式可以拆分成两个步骤
int[] arrayB; arrayB = new int[5];
- 动态初始化也可以拆分成两个步骤
int arrayC; arrayC = new int [] {1, 2, 3 };
- 静态初始化一旦使用省略格式,就不能拆分成为两个步骤了
Java 的内存需要划分成为 5 个部分:
- 栈(Srack):存放的都是方法中的局部变量,方法的运行一定在栈中
- 局部变量:方法的参数,或者是方法{} 内部的变量。
- 作用域:一旦超出作用域,立刻从栈内存当中消失。
- 堆(Heap):凡是 new 出来的东西,都在堆当中
- 堆内存里面的东西都有一个地址值:16 进制
- 堆内存里面的数据,都有默认值
- 方法区(Method Area):存储 .class 相关信息,包含方法的信息。
- 本地方法栈(Native Method Stack):与操作系统相关
- 寄存器(pc Register):与 CPU 相关
② 面向对象
局部变量和成员变量
- 定义的位置不同
- 局部变量:在方法内部
- 成员变量:在方法外部,直接写在类中
- 作用范围不同
- 局部变量:只有在方法中才可以使用,出了方法就不能用
- 成员变量:整个类全都可以通用
- 默认值不同
- 局部变量:没有默认值,如果想使用,必须手动进行赋值
- 成员变量:如果没有赋值,会有默认值,规则和数组一样
- 内存位置不一样
- 局部变量:位于栈内存
- 成员变量:位于堆内存
- 生命周期不一样
- 局部变量:随方法进栈而诞生,随着方法出栈而消失
- 成员变量:随着对象创建而诞生,随着对象被垃圾回收而消失
面向对象三大特性:封装、继承、多态
封装性在 Java 体现:
- 方法就是封装
- Private 关键字也是封装
对于基本数据类型当中的 boolean 值,Getter 方法一定要写成 isXxx 的形式,而 setXxx 规则不变
构造方法
构造方法是专门用来创建对象的方法,当我们通过关键字 new 来创建对象时,其实就是在调用构造方法
格式:
public 类名称(参数类型 参数名称)
{
方法体
}
构造方法使用注意事项:
- 构造方法的名称必须和所在的类名称完全一样,就连大小写也要一样
- 构造方法不要写返回值类型,连 Void 都不写
- 构造方法不能 return 一个具体返回值
- 如果没有编写任何构造方法,编译器会默认生成一个构造方法,无参数、无方法体
- 一旦编写了至少一个构造方法,编译器便不再自动生成
标准的类
- 所有的成员变量都要用 private 关键字修饰
- 为每一个成员变量编写一对儿 Getter/Setter 方法
- 编写一个无参数的构造方法
- 编写一个全参数的构造方法
③ 常用 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[] 字节数组
字符串常量池,在堆中开辟的一块空间。 使用 "" 直接创建的字符串都存放在字符串常量池中。
- 对于引用类型来说,“==” 进行的是地址值的比较,基本数据类型是对值的比较;
- 双引号直接创建的字符串在常量池中,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 修饰符修饰,可以继承可以重写,必须通过实现类来调用
// 静态方法,只能通过接口名调用,不可通过实现类名或者实现类对象调用
// 私有方法,私有成员方法只有默认方法可以调用、私有静态方法只有默认方法和静态方法可以调用
}
接口使用步骤:
- 接口不能直接使用,必须有一个“实现类”来“实现”该接口;格式:
public class 实现类名称 implement 接口名称
- 接口的实现类必须覆盖重写接口中所有的抽象方法。
- 创建实现类的对象,进行使用
注意事项:
- 如果实现类并没有覆盖重写接口的所有抽象方法,那么这个实现类自己就必须是抽象类。
- 接口没有静态代码块或者构造方法
- 一个类的直接父类是唯一的,但是一个类可以用时实现多个接口。格式:
public class 类名称 implements 接口 A, 接口 B {}
- 多个接口存在相同名称抽象方法,实现类只需覆盖重写一次即可。
- 如果实现类没有覆盖重写所有接口中定义的所有抽象方法,那么实现类就必须是一个抽象类。
- 如果多个接口中存在相同名称的默认方法,那么实现类一定要对冲突的默认方法进行覆盖重写。
- 一个类如果直接父类中的方法和接口当中的默认方法产生了冲突,优先用父类中的方法。(Java 继承优先级高于接口)
接口中的方法默认是抽象方法
格式: public abstract 返回值类型 方法名称(参数列表);
实现类必须覆盖重写接口所有抽象方法,除非实现类是抽象类。
接口中的默认方法
接口中的默认方法可以解决接口升级的问题。
格式:public default 返回值类型 方法名称(参数列表);
接口中静态方法
不能用接口实现类的对象来调用接口中的静态方法,应该直接使用接口名称直接调用静态方法。
格式:public static 返回值类型 方法名称(参数列表);
接口中的私有方法
抽取公共方法用来解决多个默认方法之间重复代码的问题,但这个共有方法不应该让实现类使用,应该是私有化的。
格式:private default 返回值类型 方法名称(参数列表);
、private static 返回值类型 方法名称(参数列表);
接口中的“成员变量” 即 “常量”
接口中可以定义“成员变量”,但必须使用 public static final
三个关键字进行修饰(可省略)。从效果上看,这其实就是接口的“常量”。一旦使用 final
关键字,说明不可变
格式:public static final 数据类型 名称 = 数据值;
注意事项:
- 接口中的常量,可以省略
public static final
关键字。含义不变。 - 接口中的常量,必须进行赋值,不能不赋值。
- 建议常量名称大写。多个单词用 _ 分割。
接口中的多继承
- 类与类之间是单继承的,直接父类只有一个
- 类与接口之间是多继承的,一个类可以实现多个接口
- 接口与接口之间是多继承的
- 多个父接口中的抽象方法可以重复
- 多个父接口中的默认方法如果重复,必须在子接口进行覆盖重写,并带
default
关键字。
接口其他特性
- 接口中,无法定义成员变量,但可以定义常量,其值不可改变,默认使用
public static final
修饰,可以省略、 - 接口中,没有构造方法,不能创建对象
- 接口中,没有静态代码块
⑥ 多态
对象具有多个形态
代码中体现多态性,父类引用指向子类对象。格式:父类名称 对象名 = new 子类名称();
或 接口名称 对象名 = new 实现类名称();
多态访问成员变量的两种方式:
- 直接通过对象名称访问成员变量:看等号左边是谁,优先用谁,没有则向上找。(成员变量无法进行覆盖重写)
- 间接通过成员方法访问成员变量:看该方法属于谁,优先用谁,没有则向上找。
多态访问成员方法:
看 new 的是谁,就优先用谁,没有则向上找。
口诀:
多态访问成员变量:编译看左边,运行也看左边
多态访问成员方法:编译看左边,运行看右边。
多态的好处:无论右边 new 的是哪个子类对象,左边声明的类和调用的方法可以保持一致。
对象的向上转型
其实就是多态的写法:父类名称 对象名 = new 子类名称();
含义:右侧创建一个子类对象,把它当做父类看待使用。
注意事项:向上转型一定是安全的。类似于基本数据类型的类型转换 (float -> double)
弊端:一旦向上转型为父类,那么就无法调用子类原本特有的内容。(解决方案-使用向下转型)
对象的向下转型
其实是一个还原的动作。格式:子类名称 对象名 = (子类名称)父类对象;
含义:将父类对象,还原为本来的子类对象
注意事项:
必须保证对象创建的时候就是向下转型的类型。
instanceof
关键字
返回一个 boolean 值,判断前面的对象能不能当做后面类型的实例。
格式:对象名 instanceof 类名
⑦ 其他
final
关键字
用于修饰不可改变的内容
final
用法
- 修饰类:不能有任何子类,而且一个类如果是
final
的,那么其中所有的成员方法都无法对其进行覆盖重写 - 修饰方法:这个方法不能被覆盖重写,对于类和方法而言,
abstract
关键字和final
关键字不能同时使用,因为矛盾。 - 修饰局部变量:这个变量只能被赋值一次,不能再次被修改。“一次赋值,终生不变”。对于基本数据类型,不可变值得是变量中的数据不可变;对于引用类型,不可变指的是变量中的地址值不可变。
- 修饰成员变量:这个变量只能而且必须被赋值一次,不能再次被修改。而且不再有默认值。
- 由于成员变量具有默认值,所以用了
final
之后必须手动赋值,不会再有默认值。 - 对于
final
的成员变量,要么使用直接赋值,要么使用构造方法赋值,两者取其一。 - 如果使用构造方法对
final
修饰的成员变量赋值,必须保证类中所有重载的构造方法,都最终会对final
的成员变量进行赋值。
- 由于成员变量具有默认值,所以用了
- 被
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 |
内部类
分类:
- 成员内部类
- 局部内部类(包含匿名内部类)
成员内部类
格式:
修饰符 class 外部类名称
{
修饰符 class 内部类名称
{
xxx
}
}
内部类可以直接访问外部类的成员,包括私有成员。
外部类要访问内部类的成员,必须建立内部类的对象。
即 : 内用外,随意访问。外用内需要借助内部类对象。
如何使用成员内部类
- 间接方式:在外部类的方法中,使用内部类;然后 main 只是调用外部类的方法。
- 直接方式:定义格式:
外部类名称.内部类名称 对象名 = 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 接口名称(){...}" 进行解析:
- new 代表创建对象的动作
- 接口名称就是匿名内部类需要实现哪个接口
- {...} 这才是匿名内部类的内容
注意事项:
- 匿名内部类,在【创建对象】的时候只能使用唯一一次,如果希望多次创建对象,那么就必须使用单独定义的实现类。
- 匿名对象,在【调用方法】的时候只能调用唯一一次,如果希望同一个对象调用多次方法,那么必须给对象起个名字。
- 匿名内部类是省略了【实现类/子类名称】,但是匿名对象时省略了【对象名称】,匿名内部类和匿名对象不是一回事!!
interface
作为成员变量
使用接口作为成员变量以便随时更换实现方式,这种设计更为灵活,增强了程序的扩展性。
接口作为成员变量时,对他进行复制的操作,实际上是赋给他接口的一个子类实现对象。
接口作为参数数,传递它的子类对象;
接口作为返回值类型是,也是返回它的子类对象。