Java一些基础知识
Java 语言概述
Java是由SUN(S
tanford U
niversity N
etwork 斯坦福大学网络)公司1995年推出的一门高级编程语言,2005年被Oracle公司收购。现已成为Web应用程序的首选开发语言。可以提到的是后台开发一般使用的有:Java、PHP、Python、Go、Node.js
Java面向三个不同的领域推出了不同的版本,以下为Java技术体系平台:
Java是一种面向对象的语言,可以说是在 C,C++ 的的基础上衍生出的类 C 语言,相比于 C 和 C++,Java的执行效率虽然有所降低,但开发效率比较高。Java舍弃了C语言中容易引起错误的指针
(用引用代替),增加了垃圾回收器的功能
。其中垃圾回收机制做为核心机制应该注意的是,Java 程序内部还是会存在内存泄漏和内存溢出的问题
,是因为我们写的程序有部分是不能被识别为可回收的,因此仍会存在这一问题。
Java语言具有跨平台的特点“ Write Once, Running Anywhere ”
,其原理是java是运行在(JVM J
ava V
irtual M
achine可以理解为虚拟的计算机 / 操作系统)java虚拟机上的,因此只需要针对不同的操作系统安装相应的java虚拟机即可实现跨平台性。具体参考下图:
关于Java语言的环境搭建,应该了解的有JDK和JRE。
JDK(J
ava D
evelopment K
it java开发工具包)是提供给开发人员使用的,我们使用都需要下载,其中包含了Java的开发工具(javac.exe,java.exe,javadoc.exe)
和JRE
。
JRE(J
ava R
untime E
nvironment Java运行环境)包括 Java 虚拟机 JVM
和 Java SE 标准类库(API)
,即想要运行程序就需要借助JRE。
JDK、JRE、JVM三者关系图:
注释(Comment)
1、Java规范的三种注释方式
- 单行注释 //
- 多行注释 /* */
- 文档注释(Java特有)
格式: /**
@author 指定 java 程序的作者
@version 指定源文件的版本
*/
2、单行注释和多行注释的作用:
- 对所写的程序进行解释说明,增强可读性。方便自己,方便别人。
- 调试所写的代码。
3、特点:
- 单行注释和多行注释,注释了的内容不参与编译,即编译后产生的.class结尾的字节码文件中不包含注释掉的信息。
4、文档注释的使用:
- 注释内容可以被 JDK 提供的 javadoc 所解析,生成一套以网页文件形式体现的改程序说明文档。
- 操作方式:
javadoc -d mydoc(自己起的名字) -author -version 文件名.java
生成以后打开index.html
文件
5、多行注释不能嵌套使用。
Java API 的文档
API(Application Programming Interface 应用程序编程接口)是 Java 提供的基本编程接口。
Java API 文档就相当于Java API的使用说明书,里边包含了每个类的使用说明,通过索引可以很方便的查找所需要的内容。可以类比为字典查字。
变量的分类
1、按数据类型
2、按照在类中声明的位置
基本数据类型之间的运算规则(不包含boolean):
1、自动类型提升
- 当容量小的数据类型的变量与容量大的数据类型的变量做运算时,结果自动提升为容量大的数据类型
byte、char、short-->int-->long-->float-->double
。 - 其中当byte、char、short、三种类型的变量做运算时,结果也为int。
2、强制类型转换(自动类型提升运算的逆运算)
- 需要使用强转符:()
- 注意点:强制类型转换,可能导致精度损失
说明:此时的容量大小指的是表示数的范围的大和小,比如float的容量要大于long的容量
注意:使用long对变量进行赋值时结尾要加‘L’
,否则默认为 int 类型,且数值过大超出 int 类型范围时会报错。
整型常量默认数据类型为 int ,浮点型常量默认数据类型为 double 。
String 类型变量的使用:
1、String 属于引用数据类型,称为字符串;
2、声明 String 类型变量时,使用一对 “” ;
3、String 可以和8中基本数据类型变量做运算,且运算只能是连接运算:+;
4、运算结果仍然是 String 类型。
面向对象学习
三大内容
1、Java类及类的成员:属性、方法、构造器;代码块、内部类;
2、面向对象的三大特征:封装性、继承性、多态性、(抽象性);
3、其他关键字:this、super、static、final、abstract、interface、package、import 等。
两个重要概念
1、类:对一类事物的描述,是抽象的、概念上的定义
2、对象:是实际中存在的该类事物的每个个体,因而也称为实例(instance)
- 面向对象程序设计的重点是类的设计
- 设计类,就是设计类的成员
- 对象是由类new出来的,或派生出来的;即对象是类的实例化。
类的成员
属性
匿名对象的使用
1、理解:创建的对象,没有显示地赋给一个变量名,即为匿名对象
2、特征:匿名对象只能调用一次
3、使用:如下代码
4、对属性可以赋值的位置:
- ①默认初始化
- ②显示初始化
- ③构造器中初始化
- ④有了对象以后,可以通过 “ 对象.属性 ” 或 “ 对象.方法 ” 的方式进行赋值
- ⑤在代码块中赋值
执行的先后顺序:① - ② / ⑤ - ③ - ④
package com.atguigu.special;
public class InstanceTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
//匿名对象
new phone().sendEmail();
new phone().playGame();
//错误使用,只能使用一次,无法输出被赋的值
new phone().price = 2999;
new phone().showPrice(); //输出0.0
//匿名对象的使用
phoneMall p = new phoneMall();
p.show(new phone());
}
}
class phoneMall{
public void show(phone phone) {
phone.sendEmail();
phone.playGame();
}
}
//一个电话类,描述电话的一些功能
class phone{
double price;
public void sendEmail() {
System.out.println("发送邮件");
}
public void playGame() {
System.out.println("玩游戏");
}
public void showPrice() {
System.out.println("这款手机的价格为:" + price);
}
}
//输出结果:
//发送邮件
//玩游戏
//这款手机的价格为:0.0
//发送邮件
//玩游戏
方法
方法的重载(overload)
1、定义:在同一个类中,允许存在一个以上的同名方法,只要它们的参数个数或参数类型不同即可。
- “两同一不同”:同一个类、相同方法名;参数列表不同:参数个数 / 类型不同
2、举例:Arrays 类中重载的 sort() / binarySearch()
3、判断是否重载:跟方法权限修饰符、返回值类型、形参变量名、方法体都没有关系。
重载体现:
package com.atguigu.special;
public class OverLoadTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
OverLoadTest test = new OverLoadTest();
test.getSum(1, 2); //输出:1
}
//以下四种方法构成重载
public void getSum(int i, int j) {
System.out.println("1");
}
public void getSum(double d1, double d2) {
System.out.println("2");
}
public void getSum(String s, int i) {
System.out.println("3");
}
public void getSum(int i, String s) {
System.out.println("4");
}
}
可变个数形参的方法
jdk 5.0 新增的内容,具体使用如下
- 可变个数形参的格式:数据类型 … 变量名;
public void show(String ... args){ }
- 当调用可变个数形参的方法时,传入参数个数可以是多个;
- 可变个数形参的方法与本类中方法名相同,形参不同的方法之间构成重载;
- 可变个数形参的方法与本类中方法名相同,形参类型也相同的数组之间不构成重载;
- 可变个数形参在方法的形参中,必须声明在末尾;
- 可变个数形参在方法的形参中,最多只能声明一个可变形参。
值传递机制
传递过程:
递归(recursion)方法
定义:一个方法体内调用它自身
斐波那契数列:
package com.atguigu.exer;
//斐波那契数列(1 1 2 3 5 8 13 21 34 55...)
public class Fibonacci {
public static void main(String[] args) {
// TODO Auto-generated method stub
Fibonacci test = new Fibonacci();
int f = test.fib(6); //输出:8
System.out.println(f);
}
//递归解法
public int fib(int n) {
if(n == 1) {
return 1;
}else if(n == 2) {
return 1;
}else {
return fib(n-1) + fib(n-2);
}
}
}
方法的重写(override / overwrite)
1、定义:子类继承父类以后,可以对父类中同名参数的方法进行覆盖操作。
2、应用:重写以后,当创建子类对象后,通过子类对象调用父类中的同名同参数方法时,实际执行的是子类中的方法。
3、规定:
-
方法的声明:权限修饰符 返回值类型 方法名(形参列表) throws 异常的类型{
// 方法体
}
约定俗称:子类中的叫重写的方法,父类中的叫被重写的方法 -
子类重写的方法的方法名和形参列表与父类被重写的方法名和形参列表相同
-
子类重写的方法的权限修饰符不小于父类被重写的方法的权限修饰符;特殊情况:子类不能重写父类中声明为 private 权限的方法
-
返回值类型:父类为void,则子类只能为void;父类为A类型,字子类可以是A类或A类的子类(如 Object 和 String );父类为基本数据类型(如 double),则子类也必须是相同的基本数据类型(必须也为 double)。
-
子类重写的方法抛出的异常类型不大于父类被重写的方法抛出的异常类型
-
子类和父类中的同名参数的方法要么都声明为非 static 的(考虑重写),要么都声明为 static 的(不是重写)。
构造器(构造方法、constructor)
1、构造器的作用
- 创建对象
- 初始化对象信息
2、说明
- 如果没有显示定义类的构造器,则系统默认提供一个空参构造器
- 定义构造器格式:权限修饰符 类名(形参列表){ }
- 一个类中定义的多个构造器互相构成重载
- 一旦显示了定义类的构造器后,系统就不再提供默认构造器
- 一个类中至少会有一个构造器
构造器使用:
package com.atguigu.exer;
//在该类中定义三角形的底边、高,同时声明公共方法和私有变量,提供构造器
public class TriAngle {
//属性
private double base;
private double height;
//定义构造器
public TriAngle() { //空参
}
public TriAngle(double b, double h) { //带参
base = b;
height = h;
}
//方法
public void setBase(double b) {
base = b;
}
public double getBase() {
return base;
}
public void setHeight(double h) {
height = h;
}
public double getHeight() {
return height;
}
}
package com.atguigu.exer;
//上述类的测试类
public class TriAngleTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
TriAngle t1 = new TriAngle();
t1.setBase(5);
t1.setHeight(4);
System.out.println("Base:" + t1.getBase() + ",Height:" + t1.getHeight());
TriAngle t2 = new TriAngle(3, 4); //使用定义的带参构造器创建对象并赋予初始化值
System.out.println("Base:" + t2.getBase() + ",Height:" + t2.getHeight());
}
}
//输出:
//Base:5.0,Height:4.0
//Base:3.0,Height:4.0
代码块(使用情景较低)
1、格式:一对大括号{ }
2、代码块作用:初始化类、对象
3、代码块如果有修饰的话,只能用 static
4、分类:静态代码块 vs 非静态代码块
静态代码块 | 非静态代码块 |
---|---|
内部可以有输出语句 | 内部可以有输出语句 |
随着类的加载而执行且只执行一次 | 随着对象的创建而执行 |
作用:初始化类的信息 | 每创建一个对象,就执行一次非静态代码块 |
如果一个类中定义了多个静态代码块,则按照声明的先后顺序执行 | 作用:可以在创建对象时,对对象的属性等进行初始化 |
静态代码块的执行要优先于非静态代码块的执行 | 如果一个类中定义了多个非静态代码块,则按照声明的先后顺序执行 |
静态代码块内只能调用静态的属性、静态的方法,不能调用非静态的结构 | 非静态代码块内可以调用静态的属性、静态的方法,或非静态的属性、非静态的方法 |
总结:由父及子,静态先行
内部类
1、Java 中允许将一个类 A 声明在另一个类 B 中,则类 A 就是内部类,类 B 称为外部类
2、内部类的分类:成员内部类(静态、非静态)vs 局部内部类(方法内、代码块内、构造器内)
3、成员内部类
一方面,作为外部类的成员:
- 调用外部类的结构
- 可以被 static 修饰
- 可以被4中不同的权限修饰
另一方面,作为一个类:
- 类内可以定义属性、方法、构造器等
- 可以被 final 修饰,表示此类不能被继承,不使用 final,则可被继承
- 可以被 abstract 修饰
4、关注以下问题
- 如何实例化成员内部类的对象
- 如何在成员内部类中区分调用外部类的结构
- 开发中,局部内部类的使用
注意点
:
- 在局部内部类的方法中,如果调用所在方法中的局部变量时,要求此局部变量声明为 final
- jdk7及之前版本要求声明显示为 final ,jdk8及之后的版本可以省略 final 的声明
package com.atguigu.innerclass;
public class InnerClassTest {
public static void main(String[] args) {
//实例化成员内部类的对象
//创建Dog实例(静态的成员内部类)
person.Dog dog = new person.Dog();
dog.show();
//创建Bird实例(非静态的成员内部类)
person p = new person();
person.Bird bird = p.new Bird();
bird.sing();
//在成员内部类中区分调用外部类的结构
bird.display("小白");
}
}
class person{
String name = "小黑";
int age;
public void eat() {
System.out.println("人:吃饭");
}
//静态内部类成员
static class Dog{
String name;
int age;
public void show() {
System.out.println("这是一条狗");
}
}
//非静态内部类成员
class Bird{
String name = "小黄";
public void sing() {
System.out.println("小鸟会唱歌");
person.this.eat(); //内部类中有同名方法时使用该方法调用,调用外部类的非静态属性
}
//在成员内部类中区分调用外部类的结构
public void display(String name) {
System.out.println(name); //方法的形参
System.out.println(this.name); //内部类的属性
System.out.println(person.this.name); //外部类的属性
}
}
}
package com.atguigu.innerclass;
public class InnerClassTest1 {
//开发中少见
public void method() {
//局部内部类
class A{
}
}
//开发中,局部内部类的使用
//返回一个实现了Comparable接口的类的对象
public Comparable getComparable() {
//方式一:创建一个实现Comparable接口的有名实现类的匿名对象
//创建一个实现了Comparable接口的类:局部内部类
// class MyComparable implements Comparable{
//
// @Override
// public int compareTo(Object o) {
// return 0;
// }
//
// }
//
// return new MyComparable();
//方式二:创建一个实现Comparable接口的匿名实现类的匿名对象
return new Comparable() {
@Override
public int compareTo(Object o) {
return 0;
}
};
}
}
面向对象的特征
封装与隐藏
当创建一个类的对象以后,可以通过 “对象.属性” 的方式对其属性赋值,此时赋值操作要收属性的数据类型及存储范围的制约,除此之外,没有其他约束条件。但是,在实际问题中,我们往往需要给属性赋值加入额外的限制条件,这个条件就不能在属性声明时体现,则只能通过方法进行限制条件的添加。同时,需要避免用户再使用 “对象.属性” 的方式对属性进行赋值。则需要将属性声明为私有的(private)。
此时,针对属性就体现了封装性。参考下例:
package com.atguigu.special;
public class AnimalTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
Animal a = new Animal();
a.name = "大黄";
a.age = 1;
// a.legs = 4; //设为私有的属性不可直接调用
a.setleg(-2); //通过方法调用,也起到了特定限制作用
a.show();
}
}
class Animal{
String name;
int age;
private int legs; //private修饰符将该属性设为私有
public void setleg(int l) {
if(l >= 0) {
legs = l;
}else {
legs = 0;
}
}
public void show() {
System.out.println("name:" + name + ",age:" + age + ",legs:" + legs);
}
}
//输出:name:大黄,age:1,legs:0
封装性的体现:将类的属性私有化(private),同时提供公共的(public)方法来获取(getXxx)和设置(setXxx)此属性的值。除此之外还有不对外暴露的私有方法、单列模式等。
封装性的体现,需要权限修饰符(private、缺省、protected、public)来配合。四种权限修饰符都可以用来修饰类的内部结构(属性、方法、构造器、内部类),修饰类只能用缺省、public 。
程序设计要追求“ 高内聚、低耦合 ”
(类的内部数据操作细节自己完成,不允许外部干涉,仅对外暴露少量的方法用于使用)。
this 关键字的使用
1、可以用来修饰、调用:属性、方法、构造器
2、this 修饰属性和方法(this 理解为当前对象或当前正在创建的对象(构造器中)):
- 在类的方法 / 构造器 中,可以使用 " this.属性 " 或 " this.方法 " 的方式,调用当前对象 / 当前正在创建的对象 属性或方法。但通常情况下,都选择省略。特殊情况下,如果方法 / 构造器 的形参名和类的属性名相同时,必须用 " this.变量 " 的方式,表明此变量是属性,而非形参。
3、this 调用构造器
- 在类的构造器中,可以显示的使用 " this(形参列表)" 方式,调用本类中指定的其他构造器
- 构造器中不能通过上述方式调用自己
- 如果一个类中有n个构造器,则最多有n-1构造器中使用了 " this(形参列表)"
- 规定:" this(形参列表)" 必须声明在当前构造器首行
- 构造器内部最多只能声明一个 " this(形参列表)" 用来调用其他构造器
this的使用:
package com.atguigu.exer1;
public class Boy {
private String name;
private int age;
public Boy(){
}
public Boy(String name, int age) {
this.name = name;
this.age = age;
}
public void setName(String name) {
this.name = name; //形参名与属性名相同,this表明当前类的属性
}
public String getName() {
return name;
}
public void setAge(int age) {
this.age = age;
}
public int getAge() {
return age;
}
public void marry(Girl girl) {
System.out.println("I will marry " + girl.getName());
}
public void shout() {
if(this.age >= 22) {
System.out.println("You can get marry!");
}else {
System.out.println("You can't get marry!");
}
}
}
package com.atguigu.exer1;
public class Girl {
private String name;
private int age;
public Girl(){
}
public Girl(String name, int age) {
this.name = name;
this.age = age;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void marry(Boy boy) {
System.out.println("I want to marry " + boy.getName());
boy.marry(this);
}
/**
*
* @Description 比较两个对象的大小
* @author xiaosun
* @date Jul 17, 202211:33:41 PM
* @param girl
* @return 正数:当前对象大,复数:当前对象小,0:一样大
*/
public int compare(Girl girl) {
if(this.age > girl.age) {
return 1;
}else if(this.age < girl.age) {
return -1;
}else {
return 0;
}
}
}
package com.atguigu.exer1;
//测试类
public class GirlBoyTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
Boy boy = new Boy("Tom", 20);
boy.shout();
System.out.println();
Girl girl = new Girl("Jerry", 18);
girl.marry(boy);
System.out.println();
Girl girl1 = new Girl("cherry", 23);
int compare = girl.compare(girl1);
if(compare > 0) {
System.out.println("1");
}else if(compare < 0) {
System.out.println("-1");
}else {
System.out.println("0");
}
}
}
//输出:
/*
You can't get marry!
I want to marry Tom
I will marry Jerry
-1
*/
继承性
1、继承性的好处
- 减少代码冗余,提高了代码的复用性
- 便于功能的扩展
- 为之后多态性的使用提供了前提
2、继承性的格式及体现
-
calss A extends B { };
A:子类、派生类、subclass
B:父类、超类、基类、superclass -
一旦子类A继承父类B以后,子类A中就获取了父类B中声明的所有属性和方法;特别的,父类中声明为 private 的属性或方法,子类继承父类后,仍然认为获取了父类中私有的结构,这是由于封装性的影响,使得子类不能直接调用父类的结构而已。
-
子类继承父类以后,还可以声明自己特有的属性或方法,实现功能的拓展。
注意:子类和父类的关系,不同于子集和集合的关系。
3、Java中关于继承性的规定
- 一个类可以被多个子类继承
- Java中类为单继承性:一个类只能有一个父类
- 子父类是相对的概念
- 子类直接继承的父类称为直接父类;间接继承的父类称为间接父类
- 子类继承父类以后,就获取了直接父类以及所有间接父类中声明的属性和方法
4、Object类
- 如果没有显示的声明一个类的父类,则此类继承于 java.lang.Object 类
- 所有的 java 类(除 java.lang.Object 类之外)都直接或间接的继承于 java.lang.Object 类
- 即所有的 java 类具有 java.lang.Object 类声明的功能
super 关键字的使用
1、super 理解为:父类的
2、super 可以用来调用:属性、方法、构造器
3、super 的使用:调用属性和方法
- 在子类的方法或构造器中,通过使用 “ super.属性 ” 或 “ super.方法 ” 的方式,显示的调用父类中生命的属性或方法。但通常情况下会省略 super。
- 当子类和父类中定义了同名的属性时,若想要在子类调用父类中声明的属性,则必须显示的使用 “ super.属性 ”,表明调用的是父类中声明的属性。
- 当子类重写了父类中的方法以后,若想在子类中调用父类被重写的方法时,必须显示的使用 “ super.方法 ” 的方式,表明调用的是父类中被重写的方法。
子类对象实例化的全过程
1、从结果上来看(继承性)
- 子类继承父类以后,就获取了父类中声明的属性或方法
- 创建子类的对象,在堆空间中,就会加载所有父类中声明的属性
2、从过程上来看
- 当通过子类的构造器创建子类对象时,一定会直接或间接的调用其父类的构造器,进而调用父类的父类的构造器。直到调用了 java.lang.Object 类中空参的构造器为止。正因为加载过所有的父类的结构,所以才可以看到内存中有父类中的结构,子类对象才可以考虑进行调用。
3、明确:虽然创建子类对象时调用了父类的构造器,但自始至终就创建过一个对象,即为 new 的子类对象
多态性
1、理解多态性
- 可以理解为一个事物的多种形态
2、何为多态性
- 对象的多态性:父类的引用指向子类的对象(或子类的对象赋给父类的引用)
3、多态的使用
- 虚拟方法的调用。有了对象的多态性以后,在编译器,只能调用父类中声明的方法,不能调用子类特有的属性、方法。但在运行期间,实际执行的是子类重写父类的方法。
- 总结:编译看左边,运行看右边
4、使用前提
- 类的继承关系,需要有继承关系有父类及继承它的子类
- 方法的重写,在子类中需要有重写父类中方法的方法
5、对象的多态性,只适用于方法,不适用于属性(对于属性编译和运行都看左边)
6、若要调用子类特有的属性和方法,则需要向下转型
使用强制类型转换符(强转必须要有子父类继承的关系)。使用强转时,可能出现 ClassCastException 的异常(即两个不相关的类)
instanceof 关键字的使用:
a instanceof A:判断对象a是否是类A的实例。如果是,返回true,否则返回false。
使用情况:为了避免在向下转型时出现ClassCastException的异常,在向下转型前,先进行instanceof的判断,一旦返回true,就向下转型,否则不向下转型。
如果a instanceof A返回true,则a instanceof B也返回true,则类B是类A的父类。
包装类
1、Java 提供了8种基本数据类型对应的包装类,使得基本数据类型的变量具有类的特征
2、掌握:基本数据类型、包装类、String 三者之间的相互转换
- 基本数据类型 < - - > 包装类:JDK 5.0 新特性:自动装箱、自动拆箱
- 基本数据类型、包装类 - - > String:调用 String 重载的 valueOf(Xxx xxx)
- String - - > 基本数据类型、包装类:调用包装类的 parseXxx(String s)
- 注意:转换时,可能会报 NumberFormatException 异常
static关键字
1、static 静态的
2、static 可以用来修饰:属性、方法、构造器、代码块、内部类
3、使用 static 修饰属性:静态变量(类变量)
- 属性:按是否使用 static 修饰,分为:静态变量 vs 非静态变量(实例变量)
实例变量:若创建了类的多个对象,则每个对象都独立拥有一套类中的非静态属性。当修改其中一个对象中的非静态属性时,不会导致其他对象中的属性值的改变
静态变量:若创建了类的多个对象,多个对象共享同一个静态变量。当通过某一个对象修改静态变量时,会导致其他对象调用此静态变量时,是修改过的。 - 其他说明:静态变量随着类的加载而加载;静态变量的加载要早于对象的创建;由于类只会加载一次,则静态变量在内存中也只会存在一份,存在方法区的静态域中。
是否可调用 | 类变量 | 实例变量 |
---|---|---|
类 | yes | no |
对象 | yes | yes |
4、使用 static 修饰方法:静态方法
- 随着类的加载而加载,可以通过 “ 类.静态方法 ” 的方式进行调用
是否可调用 | 静态方法 | 非静态方法 |
---|---|---|
类 | yes | no |
对象 | yes | yes |
- 静态方法中,只能调用静态的方法或属性;非静态方法中,既可以调用非静态方法或属性,也可以调用讲台方法或属性。
5、static 注意点
- 在静态方法内,不能使用 this 关键字、super 关键字
- 关于静态属性和静态方法的使用,从生命周期的角度理解
6、开发中,如何确定一个属性或方法是否要声明为 static
- 属性是可以被多个对象所共享,不会随着对象的不同而不同
- 操作静态属性的方法,通常设置为 static 的
- 工具类中的方法,习惯上声明为 static 的。例:Math、Arrays、Collections
final关键字
1、final(最终的)可以用来修饰的结构:类、方法、变量
2、final 用来修饰一个类,则此类不能被其他类所继承。例如:String 类、System类、StringBuffer 类
3、final用来修饰方法,表明此方法不可以被重写。例如:Object 类中 getClass()
4、final 用来修饰变量,此时的 “ 变量 ” 就称为是一个常量
- final 修饰属性:可以考虑赋值的位置有:显示初始化、代码块中初始化、构造器中初始化
- final 修饰局部变量:尤其是修饰形参时,表明此形参是一个常量。当调用此方法时,给常量赋一个实参,一旦赋值以后就只能在方法体内使用形参,但不能进行重新赋值
abstract 关键字
1、abstract:抽象的
2、abstract 可以用来修饰的结构:类、方法
3、abstract 修饰类:抽象类
- 此类不可实例化
- 抽象类中一定会有构造器,便于子类实例化时调用
- 开发中,都会提供抽象类的子类,让子类对象实例化,完成相关操作
4、abstract 修饰方法:抽象方法
- 抽象方法只有方法的声明,没有方法体
- 包含抽象方法的类一定是一个抽象类;反之,抽象类中可以没有抽象方法
- 若子类重写了父类中的所有抽象方法后,此子类才可实例化;若未重写父类中所有的抽象方法,则此子类也是一个抽象类,需要使用 abstract 修饰
5、abstract 使用注意点
- 不能用来修饰:属性、构造器等结构
- 不能用来修饰私有方法、静态方法、final 的方法、final 的类
接口
1、接口使用 interface 来定义
2、Java 中,接口和类是并列的两个结构
3、如何定义接口:定义接口中的成员
- JDK7及以前:只能定义全局变量和抽象方法。全局变量:public static final 的,但书写时可以省略不写;抽象方法:public abstract 的。
4、 JDK8:除了定义全局变量和抽象方法之外,还可以定义静态方法、默认方法。
- 接口中定义的静态方法只能通过接口来调用
- 通过实现类的对象,可以调用接口中的默认方法
- 如果实现类重写了接口中的默认方法,调用时,调用的是重写后的方法
- 如果子类(或实现类)继承的父类和实现的接口中声明了同名同参数的默认方法,那么子类在没有重写此方法的情况下,默认调用的是父类中的同名同参数的方法。 - - >类优先原则
- 如果实现类实现了多个接口,而这些接口中定义了同名同参数的默认方法,那么在实现类没有重写此方法的情况下,会报错。 - - >接口冲突 这就需要我们必须在现实类中重写此方法
- 在子类(或实现类)的方法中调用接口中被重写的方法:接口名.super.方法名
5、接口中不能定义构造器,意味着接口不可实例化
6、Java 开发中,接口通过让类去实现(implements)的方式来使用。如果实现类覆盖了接口中的所有抽象方法,则此实现类可以实例化;如果未覆盖所有抽象方法,则此实现类仍为一个抽象类
7、Java 类可以实现多个接口,弥补了Java 单继承性的局限性。格式:class A extends B implements C,D
8、接口与接口之间可以继承,而且可以多继承
9、接口的使用:
- 接口的使用体现多态性
- 接口,实际上就是定义了一种规范
- 开发中,体会面向对象编程
异常处理
常见异常体系结构:
java.lang.Throwable
- java.lang.Error:一般不编写针对性的代码进行处理
- java.lang.Exception:可以进行异常的处理
- 编译时异常(checked)
- IOException - - > FileNotFoundException
- ClassNotFoundException
- 运行时异常(unchecked)
- NullPointerException
- ArrayIndexOutOfBoundsException
- ClassCastException
- NumberFormatException
- InputMismatchException
- ArithmeticException
- 编译时异常(checked)
package com.atguigu.java;
import java.io.File;
import java.io.FileInputStream;
import java.util.Date;
import java.util.Scanner;
import org.junit.jupiter.api.Test;
//常见的异常
public class ExceptionTest {
//System.out.println("************以下是编译时异常************");
@Test//读数据,输入流
public void test7() {
// File file = new File("hello.txt");
// FileInputStream fis = new FileInputStream(file);
//
// int data = fis.read();
// while(data != -1) {
// System.out.print((char)data);
// data = fis.read();
// }
//
// fis.close();
}
//System.out.println("************以下是运行时异常************");
//NullPointerException空指针异常
@Test
public void test1() {
// int[] arr = null;
// System.out.println(arr[3]);
String str = "abc";
str = null;
System.out.println(str.charAt(0));
}
//IndexOutOfBoundsException索引/下标越界异常
@Test
public void test2() {
//ArrayIndexOutOfBoundsException
// int[] arr = new int[10];
// System.out.println(arr[10]);
//StringIndexOutOfBoundsException
String str = "abc";
System.out.println(str.charAt(3));
}
//ClassCastException类型转换异常
@Test
public void test3() {
Object obj = new Date();
String str = (String)obj;
}
//NumberFormatException
@Test
public void test4() {
String str = "123";
str = "abc";
int num = Integer.parseInt(str);
}
//InputMismatchException
@Test
public void test5() {
Scanner scan = new Scanner(System.in);
int score = scan.nextInt();
System.out.println(score);
scan.close();
}
//ArithmeticException
@Test
public void test6() {
int a = 10;
int b = 0;
System.out.println(a / b);
}
}
异常的处理一:抓抛模型
过程一 “ 抛 ” :程序在正常执行的过程中,一旦出现异常,就会在异常代码处生成一个对应异常类的对象。并将此对象抛出,一旦抛出对象以后,其后的代码就不再执行。
- 关于异常对象的产生:① 系统自动生成的异常对象 ② 手动的生成一个异常对象,并抛出(throw)
过程二 “ 抓 ” :可以理解为异常的处理方式 ① try - catch - finally ② throws
try - catch - finally 的使用
try{
//可能出现异常的代码
}catch(异常类型1 变量名1){
//处理异常的方式1
}catch(异常类型2 变量名2){
//处理异常的方式2
}catch(异常类型3 变量名3){
//处理异常3
}
. . .
finally{
//一定会执行的代码
}
使用说明
1、finally 是可选的
2、使用 try 将可能会出现异常的代码包装起来,在执行过程中一旦出现异常,就会生成一个对应异常类的对象,根据此对象的类型,去 catch 中进行匹配
3、一旦 try 中的异常对象匹配到某一个 catch 时,就进入 catch 中进行异常的处理,一旦处理完成,就跳出当前的 try - catch 结构(在没有写 finally 的情况),继续执行其后的代码
4、catch 中的异常类型如果没有子父类关系,则谁声明在上,谁声明在下无所谓;如果满足子父类关系,则要求子类一定声明在父类的上边,否则将会报错
5、常用的异常对象处理的方式:① String getMessage()② printStackTrace()
6、在 try 结构中声明的变量,在出了 try 结构以后,就不能再被调用
7、try - catch - finally 结构可以嵌套
8、使用 try - catch - finally 处理编译时异常,使得程序在编译时不再报错,但是运行时仍可能报错。相当于在使用该结构时,使将一个编译时可能出现的异常延迟到运行时出现
9、finally 中声明的是一定会被执行的代码,即使 catch 中又出现异常了、try 中有 return 语句、catch 中有 return 语句等情况
10、像数据库连接、输入输出流、网络编程 Scoket 等资源,JVM 是不能自动回收的,需要手动的进行资源的释放。此时的资源释放就需要声明在 finally 中
异常的处理二:throws + 异常类型
1、“ throws + 异常类型 ” 写在方法声明处,指明此方法执行时,可能会抛出异常类型。一旦方法体执行时,出现异常,仍会在异常代码处生成一个异常类的对象,此对象满足 throws 后异常类型时,就会被抛出。异常代码后续的代码就不再执行
2、try - catch - finally:真正的将异常给处理掉了;throws 的方式只是将异常抛给了方法的调用者,并没有真正处理掉
3、开发中如何选择使用 try - catch - finally 或使用 throws
- 如果父类中被重写的方法没有 throws 方式处理异常,则子类重写的方法也不能使用 throws,意味着如果子类重写的方法中有异常,必须使用 try - catch - finally 方式处理
- 执行的方法 a 中,先后又调用了另外的几个方法,这几个方法是递进关系执行的,建议这几个方法使用 throws 的方式进行处理,而执行的方法 a 可以考虑使用 try - catch - finally 方式进行处理
自定义异常类
1、继承于现有的异常结构:RuntimeException、Exception
2、提供全局变量:serialversionUID
3、提供重载的构造器