1、什么是面向对象
https://www.jianshu.com/p/7a5b0043b035
2、类和对象
面向对象的思想概述
- 类(Class)和对象(Object)是面向对象的核心概念。
- 类是对一类事物的描述,是抽象的、概念上的定义
- 对象是实际存在的该类事物的每个个体,因而也称为实例
- 万物皆对象
2.1、类的属性
一、设计类、其实就是设计类的成员
- 属性 = 成员变量 = field = 域 、字段
- 方法 = 成员方法 = 函数 = method
- 创建类的对象 = 类的实例化 = 实例化类
二、类和对象的使用
- 创建类、设计类的成员
- 创建类的对象
- 通过"对象.属性"或"对象.方法"调用对象的结构
三、如果创建了一个类的多个对象,则每个对象都独立的拥有一套类的属性。(非static的)
意味着: 如果我们修改了一个对象的属性a,则不会影响另外一个一个对象属性a的值。
# 例子
public class PersionTest {
public static void main(String[] args) {
//创建对象
Persion persion = new Persion();
//调用属性 对象.属性
persion.name = "张三";
persion.age = 18;
System.out.println("姓名: " + persion.name + "年龄: " + persion.age);
//调用方法 对象.方法
persion.eat();
persion.speak("中国话");
//创建第二个对象
Persion persion2 = new Persion();
System.out.println(persion2.name);//因为persion2对象并没有赋值所以是null
//创建第三个对象
Persion persion3 = persion1;
System.out.println(persion3.name);//将p1变量保存的对象地址值赋值给p3,导致p1和p3指向了对空间中的同一个对象 张三
persion3.name = "王五";
System.out.println(persion1.name); //p3里面改的值,p1里面也改了
}
}
class Persion{
//属性
String name;
int age;
//方法
public void eat(){
System.out.println("我正在吃饭");
}
public void speak(String language){
System.out.println("我讲的是" + language);
}
}
四、对象的内存解析
根据上面的例子画图
p3已经不算是创建一个新的对象,相当于创建一个变量,p1赋值给了p3。两者都是使用同一个堆中的值。
2.2、属性与局部变量
属性(成员变量) vs 局部变量
相同点:
- 定义变量的格式: 数据类型 变量名 = 变量值
- 先声明,后使用
- 变量都有其对应的作用域
不同点:
-
在类中声明的位置不同
- 属性: 直接定义在类的一对{ }内
class user{ int age; }
- 局部变量: 声明在方法内、方法形参、代码块内、构造形参、构造器内部的变量
-
关于权限修饰符的不同
- 属性: 可以在声明属性时,指明其权限,使用权限修饰符。常用的权限修饰符: private、public、缺省、protected —>封装性
- 局部变量: 不可以使用权限修饰符
-
默认初始化的情况
-
属性: 类的属性,根据其类型,都有默认初始化值
整型(byte、short、int、long): 0
浮点型(float、double): 0.0
字符型(char): 0 或 ‘\u0000’
布尔型(boolean): false
引用类型: null
-
局部变量: 没有默认初始化值,意味着,我们在调用局部变量之前,一定要显示赋值。特别的: 形参在调用时,我们赋值即可。
-
-
在内存中加载的位置
- 属性: 加载到堆空间中(非static)
- 局部变量: 加载到栈空间
2.2、类的方法
方法: 描述类应该具有的功能。
自己定义的方法:
class User{
public void eat(){
System.out.println("我在吃饭");
}
public String getName(String name){
return name;
}
}
方法的声明
权限修饰符 返回值类型 方法名(形参列表){ //形参列表根据情况来定有还是没有
}
注意: static、final、abstract 来修饰的方法,后面再讲。
方法的说明
-
关于权限修饰符:
- Java规定的4种权限修饰符: private、public、缺省、protected -->封装性后面细讲
-
返回值类型: 有返回值 vs 无返回值
- 如果方法有返回值,则必须在方法声明时,指定返回值的类型。同时,方法中,需要使用return关键字来返回指定类型的变量或者常量。
- 如果方法没有返回值,则方法声明时,是用void来表示。通常,没有返回值的方法中,就不需要使用return,但是,如果你非要使用,只能"return;",表示结束此方法的意思。
- 注意: 我们定义方法该不该有返回值?
- 题目需求
- 凭经验: 具体问题具体分析
-
方法名: 属于标识符,遵循标识符的规则和规范, 起的名字尽量"见名知意“
-
形参列表: 可以声明0个, 1个,或者多个形参
- 格式: 数据类型1 形参1,数据类型2 形参2 …
- 我们定义方法时,该不该定义形参?
- 题目要求
- 凭经验:具体问题具体分析
-
方法体:
- 是方法中功能的体现,通过循环分支、条件判断等语句完成复杂的逻辑关系。
- 方法中可以调用其他方法,同类中可以直接调用,不同类中通过类的实例化对象调用。
- 注意:方法中不可以定义新的方法。
-
return关键字的使用:
- 使用范围: 使用在方法体中
- 作用:
- 结束方法
- 针对于有返回值类型的方法,使用"return"数据"方法返回所要的数据"。
- 注意点: return关键字后面不可以声明执行语句。
方法的使用
- 同类中的方法可以直接调用当前类的属性或方法,不同类中通过类的实例化对象调用。
- 特殊的:方法A中又调用了自身—递归方法。(自身调用)
2.5、匿名对象
- 理解: 我们创建的对象,没有显示的赋给一个变量名。既为匿名对象
- 特征:匿名对象只能调用一次
- 使用
public class Dict {
public static void main(String[] args) {
new DictTest().eat();//没有赋值一个变量名直接调用就是匿名对象
}
}
class DictTest{
public void eat(){
System.out.println("我正在吃饭");
}
2.6、方法的重载
定义
如果有两个方法的方法名相同,但参数不一致,那么可以说一个方法是另一个方法的重载。
具体说明如下:
-
“两同一不同”:
- 同一个类、相同方法名
- 参数列表不同: 参数个数不同,参数类型不同
public class Load { public void getSum(int a){ } public void getSum(int a,int b){ } public void getSum(String a){ } public void getSum(String a,Double c){ } }
判断是否是重载
跟方法的权限修饰符、返回值类型、形参变量名、方法体都没有关系!
在通过对象调用方式时,如何确定某一个指定的方法
方法名 —> 参数列表
2.7、新特性 : 可变个数形参的方法
- jdk 5.0新增的内容
- 具体使用:
- 可变个数形参的格式: 数据类型 … 变量名
- 当调用可变个数形参的方法时,传入的参数的个数可以是: 0个,1个,多个
- 可变个数形参的方法与本类中方法名相同,形参不同的方法之间构成重载。
- 可变个数形参的方法与本类中方法名相同,形参类型也相同的数组之间不构成重载。换句话说,二者不能共存。
- 可变个数形参在方法的形参中,必须声明在末尾。
- 可变个数形参在方法的形参中,最多只能声明一个可变形参。
public class Load {
public static void main(String[] args) {
New a = new New();
a.getName("张三");
}
}
class New{
// public void getName(String name){
//
// }
public void getName(String ... name){
//需要遍历不遍历输出的值是地址值
for (int i=0; i<name.length; i++){
System.out.println(name[i]);
}
}
// public void getName(String[] name){
// 相当于上面,二者相同
// }
//可变性参数只能写在末尾
public void getName(int age,String ... name){
}
//可变个数形参在方法的形参中,最多只能声明一个可变形参。
//多了否则会报错
// public void getName(String ... name,int ... age){
//
// }
}
3、封装
封装概述
是指隐藏对象的属性和实现细节,仅对外提供公共访问方式,这里就会用到一个关键字是private,在后面的使用成员变量时候,不能直接调动,只能通过方法调用;
封装的优点
-
- 良好的封装能够减少耦合。
-
- 类内部的结构可以自由修改。
-
- 可以对成员变量进行更精确的控制。
-
- 隐藏信息,实现细节。
封装性的体现
一、我们将类的属性私有化(private),同时,提供公共的(public)方法来获取(getXxx)和设置(setXxx)此属性的值。
二、不对外暴露的私有的方法
三、单例模式(将构造器私有化)
四、如果不希望类在包外被调用,可以将类设置为缺省的。
一个标准案例的使用
public class AnimalTest {
public static void main(String[] args) {
Animal animal = new Animal();
animal.setName("大黄");
System.out.println(animal.getName());
}
}
class Animal{
private String name;
private int age;
private int leg;//腿
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public int getLeg() {
return leg;
}
public void setLeg(int leg) {
this.leg = leg;
}
}
3.1、Java规定的四种权限修饰符
封装性体现,需要权限修饰符来配合
- Java规定的四种权限(从小到大排序): private、缺省、protected、public
- 权限从小到大顺序为:private < 缺省 < protected < public
- 具体修饰的范围:
2.四种权限可以用来修饰类及类的内部结构: 属性、方法、构造器、内部类
3.具体的,4种权限都可以用来修饰类的内部结构: 属性、方法、构造器、内部类
3.1 修饰类的话,只能使用: 缺省、public 例如 class Dog{} public class Cat{}
总结封装性: Java提供了四种权限修饰符来修饰类及类的内部结构,体现类及类的内部结构在调用时的可见性的大小。
3.2、构造器
构造器的作用
- 创建对象
- 初始化对象的信息
# 创建类的对象: new + 构造器
Persion p = new Persion()
说明
- 如果没有显示的定义类的构造器的话,则系统默认提供一个无参的构造器。
- 定义构造器的格式: 权限修饰符 类名(形参列表){}
# 修饰符公共的 有参构造器
public Persion(int age) {
this.age = age;
}
3.一个类中定义的多个构造器,彼此构成重载。
4.一旦我们定义了类的构造器之后,系统就不再提供默认的无参构造器
5.一个类中,至少会有一个构造器。可能是自己定义的,也可能是系统提供的。
3.3、JavaBean(扩展知识)
- JavaBean是一种Java语言写成的可重用组件。
- 所谓JavaBean,是符合如下标准的Java类
- 类是公共的
- 有一个无参的公共的构造器
- 有属性,且有对应的get、set方法
public class Persion {
private String name;
public Persion() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
- 用户可以使用JavaBean将功能、处理、值、数据库访问和其他任何可以用Java代码创造的对象进行打包,并且其他的开发者可以通过内部的JSP页面、Servlet、其他JavaBean、applet程序或者应用来使用这些对象。用户可以认为JavaBean提供了一种随时随地的复制和粘贴的功能,而不用关心任何改变。
3.4、this关键字
this关键字的使用
- this可以用来修饰 调用: 属性、方法、构造器
this修饰属性和方法
this理解为: 当前对象
在类的方法或者构造器中,我们可以使用"this.属性" 或"this.方法"的方式,调用当前对象属性或方法。但是,通常情况下,我们都选择省略"this."。特殊情况下,如果方法或构造器的形参和类的属性同名时,我们必须显示的使用"this.变量"的方式,表名此变量是属性,而非形参,例如:
# this指向的是属性age而非形参里面的age
public class Persion {
private String name;
public Persion(String name) {
this.name = name;
}
}
this调用构造器
- 我们在类的构造器中,可以显示的使用"this(形参列表)"方式,调用本类中指定的其他构造器
- 构造器中不能通过"this(形参列表)"方式调用自己
- 如果一个类中有n个构造器,则最多有n-1构造器中使用了"this(形参列表)"
- 规定: "this(形参列表)"必须声明在当前构造器的首行
- 构造器内部: 最多只能声明一个"this(形参列表)",用来调用其他的构造器
3.5、package和import的使用
package关键字的使用
- 为了更好实现项目中类的管理,提供包的概念
- 使用package声明类或接口所属的包,声明在源文件的首行
- 包,属于标识符,遵循标识符的命名规则、规范、“见名知意”
- 每" . "一次,就代表一层文件目录。
补充: 同一个包下,不能命名同名的接口、类。
不同的包下,可以命名同名的接口、类。
import关键字的使用
- 在源文件中显示的使用import结构导入指定包下的类、接口
- 声明在包的声明和类的声明之间
- 如果需要导入多个结构,则并列写出即可。
- 可是使用"xxx.*"的方式,表示可以导入xxx包下的所有结构
- 如果使用的类或接口是java.lang包下定义的,则可以省略import结构
- 如果使用的类或接口是本包下定义的,也可以省略import结构
- 如果在源文件中,使用了不同包下的同名的类,则必须至少有一个类需要以全类名的方式显示。 全类名格式: java.util.Scanner sc = new java.util.Scanner(System.in);
- 使用“ xxx.* ”方式表明可以调用xxx包下的所有结构,但是如果使用的是xxx子包下的结构,则仍需要显示导入
- import static: 导入指定类或接口中的静态结构: 属性或方法(用的少,基本不用。)
4、继承
4.1、继承的概念
继承就是子类继承父类的特征和行为,使得子类对象(实例)具有父类的实例域和方法,或子类从父类继承方法,使得子类具有父类相同的行为。
4.2、继承性的好处
- 减少代码的冗余,提高了代码的复用性
- 便于功能的扩展
- 为之后多态性的使用,提供了前提
4.3、类的继承格式
一、在 Java 中通过 extends 关键字可以申明一个类是从另外一个类继承而来的,一般形式如下:
public class 父类 {
}
public class 子类 extends 父类 {
}
体现: 一旦子类继承父类之后,子类就获取了父类中声明的所有的属性和方法。
特别的,父类中声明为private的属性或方法,子类继承父类以后,仍然认为获取了父类中私有的结构。只是因为封装性的影响,使得子类不能直接调用父类的结构而已。
注意: 父类中私有的方法不可以直接调用了,只能在父类中定义一个公共的方法,内部调用私有方法,例如:
public void study(){
System.out.println("正在学习");
eat();
}
private void eat(){
System.out.println("正在吃饭");
}
二、子类继承父类以后,还可以声明自己特有的属性或方法: 实现功能的扩展
# 在继承父类的基础上扩展功能
public class Dog extends Animal {
public int age;
public void sleep(){
System.out.println("我在睡觉");
}
}
三、需要注意的是Java不支持多继承,但支持多重继承
四、
- 如果我们没有显示的声明一个类的父类的话,则此类继承与java.lang.object类
- 所有的java类(除java.lang.object类之外)都直接或间接的继承于java.lang.object类
- 意味着,所有的java类具有java.lang.object类声明的功能
4.4、方法的重写
定义
在子类中可以根据需要对从父类中继承来的方法进行改造,也称为方法的 重置、覆盖。在程序执行时,子类的方法将覆盖父类的方法。
要求
- 子类重写的方法必须和父类被重写的方法具有相同的方法名称、参数列表
- 子类重写的方法的返回值类型不能大于父类被重写的方法返回值类型
- 例如: 子类返回值类型是String那么父类返回值类型就是Object,子类返回值类型不能比父类返回值类型发
- 尽量返回值类型写相同(除非特殊要求不一样)
- 子类重写的方法使用的访问权限不能小于父类被重写的方法的访问权限
- 子类不能重写父类中声明为private权限的方法
- 子类方法抛出的异常不能大于父类被重写方法的异常
注意:
子类与父类中同名同参数的方法必须同时声明为非static的(即为重写),或者同时声明为static的(不是重写),因为static方法是属于类的,子类无法覆盖父类中的方法。
应用
1.重写以后,当创建子类对象以后,通过子类对象调用子父类中同名同参数的方法时,实际执行的是子类重写父类的方法。
public class father {
public void eat(){
System.out.println("我在吃饭");
}
}
public class son extends father {
public void eat(){
System.out.println("儿子在吃饭");
}
}
public class familyTest {
public static void main(String[] args) {
son son = new son();
son.eat(); //调用的是子类重写过后的方法 儿子在吃饭
}
}
4.5、super
super关键字的使用
- super理解为: 父类的
- super可以用来调用: 属性、方法、构造器
- super的使用
- 我们可以在子类的方法或构造器中。通过使用"super.属性"或"super.方法"的方式,显示的调用父类中声明的属性或方法。但是,通常情况下,我们习惯省略"super"
- 特殊情况:当子类和父类中定义了同名的属性时,我们要想在子类中调用父类中声明的属性,则必须显示的使用super.属性的方式,表名调用的是父类中声明的属性。
- 特殊情况: 当子类重写了父类中的方法以后,我们想在子类的方法中调用父类中被重写的方法时,则必须显示的使用"super.方法"的方式,表名调用的是父类中被重写的方法。
代码案例:
class Animal{
String name = "动物";
int age;
int id = 1001;
public void eat(){
System.out.println("动物们在吃饭");
}
}
class Dog extends Animal{
String sex;
int id = 1002;
public void show(){
System.out.println(name);//因为子类和父类之间并没有同名的name属性,所以调用name可以省略super
System.out.println(super.id);//通过super获取父类的id
}
public void eat(){
super.eat();//调用父类中的eat方法
//System.out.println("狗在吃饭");
}
}
public class SuperTest {
public static void main(String[] args) {
Dog dog = new Dog();
dog.show();//动物 1001
dog.eat();// 动物们在吃饭
}
}
4.super调用构造器
super调用构造器理解: https://blog.csdn.net/qq_34115899/article/details/80325079
- 我们可以在子类的构造器中显示的使用"super(形参列表)"的方式,调用父类中声明的指定的构造器。
- "super(形参列表)"的使用,必须声明在子类构造器的首行!
- 我们在类的构造器中,针对于"this(形参列表)"或"super(形参列表)"只能二选一,不能同时出现
- 在构造器的首行,没有显示的声明"this(形参列表)“或"super(形参列表)”,则默认调用的是父类中无参的构造器: super( )
5、多态
5.1、多态性的使用前提
- 继承
- 重写
- 父类引用指向子类对象
5.2、多态的优点
- 消除类型之间的耦合关系
- 可替换性
- 可扩充性
- 接口性
- 灵活性
- 简化性
5.3、什么是多态
-
理解多态性: 可以理解为一个事物的多种形态。
-
何为多态性:
-
对象的多态性: 父类的引用指向子类的对象(或子类的对象赋给父类的引用)
-
# 例子 Persion是父类,Man是子类。 Persion s1 = new Man();
-
-
多态的使用: 虚拟方法调用
- 有了对象的多态性以后,我们在编译期,只能调用父类中声明的方法,但在运行期,我们实际执行的是子类重写父类的方法。
- 总结: 编译,看左边,运行看右边。左边是父类,右边是子类。
-
对象的多态性,只适用于方法,不适用于属性。
# 示例
class Persion{
int id = 1001;
}
class Man extends Persion{
int id = 1002;
}
public class PersionTest {
public static void main(String[] args) {
Persion p = new Man();
System.out.println(p.id); //父类的1001
}
}
5.4、instanceof操作符
使用情景: 为了避免在向下转型时出现ClassCastException异常,我们在向下转型之前,先进行instanceof的判断,一旦返回true,就进行向下转型。如果返回false,不进行向下转型。
public class PersionTest {
public static void main(String[] args) {
Persion p = new Man();
Persion p1 = new Woman();
// p.play();
//想使用子类里面的其它方法需要向下转型,使用instanceof进行判断
//p1 new的对象是Woman,也只能调用Woman里面的方法
//注意: 不能用p1 new Woman对象,来判断向下转型Man
if (p1 instanceof Woman){
Woman woman = (Woman)p1;
woman.eat();
}
if (p instanceof Man){
Man man = (Man)p; //向下转型
man.eat();
}
}
}
6、Object类的使用
-
Object类是所有Java类的根父类
-
如果在类的声明中未使用extedns关键字指明其父类,则默认父类为java.lang.Object类
-
public class Persion{ } //等价于 public class Persion extends Object{ }
6.1、equals()
-
是一个方法,而非运算符
-
只能适用于引用数据类型
-
Object类中equals()的定义
public boolean equals(Object obj){ return(this == obj); }
说明: Object类中定义的equals()的作用是,比较两个对象的地址值是否相同。和==相同。
-
像String、Date、File、包装类等都重写了Object类中equals()方法。重写以后,比较的不是两个引用的地址是否相同,而是比较两个对象的"实体内容"是否相同。
-
通常情况下,我们自定义的类如果使用equals()的话,也通常是比较两个对象的"实体内容"是否相同。那么,我们就需要对Object类中的equals()进行重写。
class Man{
String name;
int age;
public Man(String name, int age) {
this.name = name;
this.age = age;
}
//手动重写equals (开发建议使用开发工具自动生成的equals)
@Override
public boolean equals(Object o) {
if (this == o){
return true;
}
if (o instanceof Man){
Man man = (Man)o;
return this.age == man.age && this.name.equals(man.name);
}else {
return false;
}
}
}
public class PersionTest {
public static void main(String[] args) {
Man man = new Man("王五",20);
Man man1 = new Man("王五",20);
System.out.println(man.equals(man1));
}
}
6.重写equals(方法的原则
- 对称性: 如果x.equals(y) 返回是" true “,那么y.equals( x )也应该返回是” true "
- 自反性: x.equals(x) 必须返回是" true "
- 传递性: 如果x.equals(y)返回是" true “,而且y.equals(z)返回是” true “,那么z.equals( x )也应该返回是” true "
- 一致性: 如果x.equals(y)返回是" true “,只要x和y内容一直不变,不管你重复x.equals(y)多少次,返回都是” true "。.
- 任何情况下,x.equals( null ),永远返回是"false"。
- x.equals(和x不同类型的对象)永远返回是"false"。
6.2、toString()
-
当我们输出一个对象的引用时,实际上就是调用当前对象的toString( )
-
Object类中的toString()的定义
-
public String toString() { return getClass().getName() + "@" + Integer.toHexString(hashCode()); }
-
-
像String、Date、File、包装类都重写了Object类中的toString()方法,使得在调用对象的 toString()时,返回"实体内容"信息。
-
自定义类重写toString()方法
-
class Man{ String name; int age; @Override public String toString() { return "Man{" + "name='" + name + '\'' + ", age=" + age + '}'; } } public class PersionTest { public static void main(String[] args) { Man man = new Man();' //调用的就是重写之后的toString()方法而不是Object里面的toString()方法了 System.out.println(man.toString()); } }
-
7、单元测试的使用
步骤
-
导入Junit4包 (推荐Maven导入方式)
<dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.13</version> <scope>test</scope> </dependency>
-
创建Java类,进行单元测试。
- 此时的Java类要求,1.此类是public 2.此类提供公共的无参的构造器
-
此类中声明单元测试方法
- 此时的单元测试方法,方法的权限是public,没有返回值,没有形参
-
此单元测试方法上需要声明注释 @Test,并在单元测试类中导入包: import org.junit.Test;
package Excel;
import org.junit.Test;
public class PersionTest {
@Test
public void test(){
int a = 0;
System.out.println(a);
}
}
好处
- 方便测试
- 减少bug,快速定位bug
- 提高工作效率
- 提高代码质量
8、包装类
基本数据类型 | 包装类 |
---|---|
byte | Byte |
short | Short |
int | Integer |
long | Long |
float | Float |
double | Double |
boolean | Boolean |
char | Character |
包装类的使用:
- java提供了8种基本数据类型对应的包装类,使得基本数据类型的变量具有类的特征
- 掌握的: 基本数据类型、包装类、String三者之间的相互转换
public class WapperTest {
//基本数据类型 -- >包装类;调用包装类的构造器
@Test
public void test1(){
int num = 10;
int num2 = 10;
Integer integer = new Integer(num);
Integer integer1 = new Integer(num2);
System.out.println(integer.toString());
System.out.println(integer.equals(integer));//包装了重写equals方法比较的是内容了
}
//包装类 --->基本数据类型:调用包装类的xxxValue()
@Test
public void test2(){
Integer integer = new Integer(1);
int num = integer.intValue();
System.out.println(integer);
}
//JDK 5.0新特性:
@Test
public void test3(){
//自动装箱
int num = 10;
Integer integer = num;
System.out.println(integer);
//自动拆箱
Integer integer1 = new Integer(20);
int num2 = integer1;
System.out.println(num2);
}
//基本数据类型、包装类 --- >String类型,调用String重载的valueOf()
@Test
public void test4(){
int num = 10;
String s = String.valueOf(num);
System.out.println(s);
String s1 = String.valueOf(new Integer(20));
System.out.println(s1);
}
//String类型 ---> 基本数据类型、包装类
@Test
public void test5(){
String s = "123";
int i = Integer.parseInt(s);
System.out.println(i);
}
}
9、static关键字
-
static: 静态的
-
static可以用来修饰: 属性、方法、代码块、内部类
-
使用static修饰属性: 静态变量 或者称为(类变量)
-
属性:按是否使用static修饰,又分为: 静态属性 vs 非静态属性(实例变量)
- 实例变量: 我们创建了类的多个对象,每个对象都独立的拥有一套类的非静态属性。当修改其中一个对象的非静态属性时,不会导致其他对象中同样的属性值修改。
- 静态变量: 我们创建了类的多个对象,多个对象共享同一个静态变量。当通过某一个对象修改静态变量时,会导致其他对象调用此静态变量时,是修改过了的。注意:多个对象是同一个类声明的多个对象,例如。 A a = new A() A a2 = new A()
-
static修饰属性的其他说明
-
静态变量随着类的加载而加载。可以通过"类.静态变量"的方式进行调用
-
静态变量的加载要早于对象的创建
-
由于类只会加载一次,则静态变量在内存中也只会存在一份: 存在方法区的静态域中。
-
例子 public class ChineseTest { public static void main(String[] args) { Chinese chinese = new Chinese(); Chinese chinese1 = new Chinese(); chinese1.name="法国"; System.out.println(Chinese.name);//通过类名.静态变量名调用 法国 System.out.println(chinese.name);//法国 System.out.println(chinese1.name);//法国 当静态变量值被修改,那么所有对象的值都会被修改,因为被static修饰的变量是共享的。 } } class Chinese{ //静态变量 static String name = "中国"; }
-
-
-
使用static修饰方法: 静态方法
-
随着类的加载而记载,可以通过"类.静态方法"的方式进行调用
-
例子 public class ChineseTest { public static void main(String[] args) { Chinese.eat(); } } class Chinese{ //静态方法 public static void eat(){ System.out.println("中国人吃中餐"); } }
-
静态方法中,只能调用静态的方法或属性,例子:
-
-
非静态方法中,既可以调用非静态的方法或属性,也可以调用静态的方法或属性,例子:
-
-
-
static注意点:
- 在静态的方法内, 不能使用this关键字、super关键字
-
开发中,如何确定一个属性是否要声明static
- 属性是可以被多个对象所共享的,不会随着对象的不同而不同的。
- 被final修饰的变量叫常量,也常常声明为static(后面讲)。static final。
- 开发中,如何确定一个方法是否要声明为static
- 操作静态属性的方法,通常设置为static的
- 工具类中的方法,习惯上声明为static,方便调用。
10、代码块(重点)
-
代码块的作用: 用来初始化类、对象
-
代码块如果有修饰的话,只能使用static
-
分类: 静态代码块和非静态代码块
-
静态代码块
-
内部可以有输出语句
-
随着类的加载而执行,只执行一次
-
调用类的静态方法加载静态的代码块,只执行一次。 static { System.out.println("我是静态代码块"); }
-
作用: 初始化类的信息,例如被static修饰的属性、方法。
-
如果一个类中定义了多个静态代码块,则按照声明的先后顺序执行
-
静态代码块的执行要优先于非静态代码块的执行
-
静态代码块内只能调用静态的属性、静态的方法,不能调用非静态的结构
-
-
非静态代码块
-
内部可以有输出语句
-
随着对象的创建而执行,每创建一个对象,就会执行一次非静态代码块。
-
创建完对象才会加载非静态代码块,并且每造一个对象,都会加载一次非静态代码块 { System.out.println("我是非静态代码块"); }
-
作用: 可以在创建对象时,对对象的属性或方法等进行初始化。
-
如果一个类中定义了多个非静态代码块,则按照声明的先后顺序执行。
-
非静态代码块内可以调用静态的属性、静态的方法,或非静态的属性、非静态的方法。
-
-
11、final关键字
- final可以用来修饰的结构: 类、方法、变量
- final 用来修饰一个类
-
被修饰的类不能被其它类所继承
-
-
比如: String类、System类、StringBuffer类,源代码中已经被final修饰了。
-
- final 用来修饰方法
-
被修饰的方法不能被重写
-
-
比如: Object类中的getClass();
-
- final 用来修饰变量
- 被修饰的变量就称为一个常量,并且变量名改为大写(规范)。
- 修饰属性,可以考虑赋值的位置有: 显示初始化、代码块中初始化、构造器中初始化。
- 修饰局部变量: 尤其是使用final修饰形参时,表明此形参时一个常量。当我们调用此方法时,给常量形参赋一个实参,一旦赋值以后,就只能在方法体内使用此形参,但不能进行重新赋值。
- 被 static final 修饰的属性称为全局常量
12、抽象类与抽象方法
abstract关键字的使用
- abstract: 抽象的
- abstract可以用来修饰的结构: 类、方法
- abstract修饰类: 抽象类
- 被修饰的类不能实例化(不可以new对象)
- 抽象类中一定有构造器,便于子类实例化时调用(涉及,子类对象实例化的全过程)
- 开发中,都会提供抽象类的子类,让子类对象实例化,完成相关的操作
- abstract修饰方法: 抽象方法
- 抽象方法只有方法的声明,没有方法体。例如 public abstract void eat();
- 包含抽象方法的类,一定是一个抽象类,反之,抽象类中可以没有抽象方法。
- 父类中有抽象方法,子类必须要重写。
- 若子类没有重写父类中的所有的抽象方法,则此子类也是一共抽象类,需要使用abstract修饰
- 带有static,final的方法不能被修饰为抽象方法
- 基本使用
public class AbstractTest {
public static void main(String[] args) {
Student student = new Student();
student.eat();
}
}
abstract class Persion{
String name;
int age;
public Persion() {
}
public Persion(String name, int age) {
this.name = name;
this.age = age;
}
public abstract void eat();
}
class Student extends Persion{
@Override
public void eat() {
System.out.println("学生要吃有营养的饭");
}
}
13、接口
接口的使用
-
接口使用interface来定义
-
Java中,接口和类是并列的两个结构,(同级别)
-
如何定义接口: 定义接口中的成员
-
JDK7及以前: 只能定义全局常量和抽象方法
- 全局常量: public static final的,可以省略不写,默认。
- 抽象方法: public abstract 的,可以省略不写,默认。
-
JDK8: 除了定义全局常量和抽象方法之外,还可以定义静态方法、默认方法(新特性)。
-
接口中定义的静态方法,只能通过接口来调用。也就是(接口名.方法名)。
-
通过实现类的的对象,可以调用接口中的默认方法。
-
如果实现类重写了接口的默认方法,调用时,仍然调用的是重写以后的方法。
-
如果子类继承父类和实现的接口中声明了同名同参数的方法,那么子类在没有重写此方法的情况下,默认调用的是父类中的同名同参数的方法。
-
子类的方法中调用接口中的默认方法,格式: 接口名.super.方法名
-
例子 public class InterfaceTest2 { public static void main(String[] args) { Animal.eat();//动物们要吃好吃的 new Cat().play();//狗在玩游戏 new Cat().run();//狗在跑步 new Cat().sleep(); } } interface Animal{ //静态方法 public static void eat() { System.out.println("动物们要吃好吃的"); } //默认方法 public default void play(){ System.out.println("动物们正在玩游戏"); } public default void run(){ System.out.println("动物们在跑步"); } public default void gram(){ System.out.println("动物们在玩游戏"); } } class Dog{ public void run(){ System.out.println("狗在跑步"); } } class Cat extends Dog implements Animal{ //重写之后的方法 public void play(){ System.out.println("猫在玩游戏"); } public void sleep(){ System.out.println("狗在睡觉"); //调用接口中的方法 Animal.super.gram(); } }
-
-
-
接口中不能定义构造器,意味着接口不可以实例化。
-
Java开发中,接口通过让类去实现(implements)的方式来使用
-
如果实现类覆盖了接口中的所有的抽象方法,则此实现类就可以实例化
-
如果实现类没有覆盖接口中所有的抽象方法,则此实现类仍为一个抽象类
-
简单明了,如果一个类想使用一个接口,那么必须要重写接口里面的抽象方法。
-
例子 public class InterfaceTest { public static void main(String[] args) { Cat cat = new Cat(); System.out.println(cat.mode); cat.eat(); } } interface Fly{ String mode = "老虎"; //省写了 public static final void eat();//省写了 public abstract } class Cat implements Fly{ @Override public void eat() { System.out.println("猫吃猫粮"); } }
-
-
Java类可以实现多个接口 -->弥补了Java单继承的局限性
-
格式一 class A extends B implements C,D
-
class Kit{ } interface Wrench{ } interface Insertion{ } class Comprehensive extends Kit implements Wrench,Insertion{ }
-
格式二 class A implements B,C
-
interface Wrench{ } interface Insertion{ } class Comprehensive implements Wrench,Insertion{ }
-
-
接口与接口之间可以继承,而且可以多继承
interface Car{ } interface Bike{ } interface Foot extends Car,Bike{ }