万物皆对象
描述一个物质都可以通过两方面说明:数据模型(属性)、行为模型(行为)。
在Java编程中,我们使用成员变量表示数据模型,用成员方法表示行为模型。
使用类表示某些具有相同属性和行为的事物。
一、类
1.变量
(1)局部变量
- 声明在构造方法、静态方法、实例方法、代码块中的变量,都是局部变量;
- 不能使用static和访问修饰符修饰;
- 可以使用final修饰,即为常量,不必在声明语句中赋值;
- 当执行局部变量所在的方法或代码块时,才有机会被创建,在方法或代码块执行结束后被自动销毁;
- 局部变量在内存的栈区分配;
- 局部变量在使用之前必须要先赋值;
(2)实例变量
- 声明在所有方法体和代码块之外,并且没有使用static修饰的变量,叫做实例变量;
- 可以使用访问修饰符和final修饰;
- 使用final修饰时,一定要赋值;
- 实例变量是在对象被创建时创建,对象被销毁时销毁;
- 作用域范围在整个类中;
(3)类的变量
- 声明在所有方法体和代码块之外,并且使用static修饰的变量;
- 可以使用访问修饰符修饰;
- 一般配合final使用,即public static fianl,标识符使用大写;
- 类变量被分配在静态存储区,是被所有该类的对象共享数据;
- 类变量是在程序开始时被创建,程序结束时销毁;
2.构造方法 构造方法就是类构造对象时调用的方法,主要用来实例化对象。
- 构造方法作用:(1).构造出来一个类的实例 (2).对构造出来个一个类的实例(对象)初始化。
- 构造方法的名字必须与定义他的类名完全相同,没有返回类型,甚至连void也没有
- 主要完成对象的初始化工作,构造方法的调用是在创建一个对象时使用new操作进行的
- 类中必定有构造方法,若不写,系统自动添加无参构造方法。接口不允许被实例化,所以接口中没有构造方法
- 不能被static、final、synchronized、abstract和native修饰
- 构造方法在初始化对象时自动执行,一般不能显式地直接调用.当同一个类存在多个构造方法时,java编译系统会自动按照初始化时最后面括号的参数个数以及参数类型来自动一一对应。完成构造函数的调用
- 构造方法分为两种:无参构造方法 有参构造方法
- 构造方法可以被重载。没有参数的构造方法称为默认构造方法,与一般的方法一样,构造方法可以进行任何活动,但是经常将他设计为进行各种初始化活动,比如初始化对象的属性
-
子类继承父类中,
***子类的实例化过程
***构造方法不能被子类继承
***子类创建对象时,会先去创建父类的对象。
默认是去调用父类的无参构造方法。
***子类构造方法中,第一行默认是super()
***为什么子类中第一行会默认有super()
因为他继承父类的成员使用,使用前这些成员必须初始化,
而他们是父类的成员,所以,必须通过父类进行初始化。
所以,会先创建一个父类的对象。
**当父类没有无参构造方法时必须使用this或者super调用其他的构造方法。 - 自定义类中,如果不写构造方法,java系统会默认添加一个无参的构造方法。如果写了一个有参的构造方法,就一定要写无参构造方法。
如果想使用无参的构造方法,就必须手动给出无参构造方法。
建议:一般情况下,我们自定义的类都要手动给出无参构造方法
3.访问修饰符 前面博客已写出这里不再写
4.代码块
(1)静态代码块
- 格式 :在java类中(方法中不能存在静态代码块)使用static关键字和{}声明的代码块
- 执行时机:静态代码块在类被加载的时候就运行了,而且只运行一次,并且优先于各种代码块以及构造函数。如果一个类中有多个静态代码块,会按照书写顺序依次执行。后面在比较的时候会通过具体实例来证明。
- 静态代码块的作用:一般情况下,如果有些代码需要在项目启动的时候就执行,这时候就需要静态代码块。比如一个项目启动需要加载的很多配置文件等资源,我们就可以都放入静态代码块中
- 静态代码块不能存在于任何方法体中:
这个应该很好理解,首先我们要明确静态代码块是在类加载的时候就要运行了。我们分情况讨论:
对于普通方法,由于普通方法是通过加载类,然后new出实例化对象,通过对象才能运行这个方法,而静态代码块只需要加载类之后就能运行了。
对于静态方法,在类加载的时候,静态方法也已经加载了,但是我们必须要通过类名或者对象名才能访问,也就是说相比于静态代码块,静态代码块是主动运行的,而静态方法是被动运行的。
不管是哪种方法,我们需要明确静态代码块的存在在类加载的时候就自动运行了,而放在不管是普通方法还是静态方法中,都是不能自动运行的。
- 静态代码块不能访问普通变量:这个理解思维同上,普通变量只能通过对象来调用,是不能放在静态代码块中的
(2)构造代码块
- 格式:在java类中使用{}声明的代码块(和静态代码块的区别是少了static关键字)
- 执行时机:构造代码块在创建对象时被调用,每次创建对象都会调用一次,但是优先于构造函数执行。需要注意的是,听名字我们就知道,构造代码块不是优先于构造函数执行,而是依托于构造函数,也就是说,如果你不实例化对象,构造代码块是不会执行的,如果存在多个构造代码块,则执行顺序按照书写顺序依次执行
- 构造代码块的作用:
和构造函数的作用类似,都能对对象进行初始化,并且只要创建一个对象,构造代码块都会执行一次。但是反过来,构造函数则不一定每个对象建立时都执行(多个构造函数情况下,建立对象时传入的参数不同则初始化使用对应的构造函数),利用每次创建对象的时候都会提前调用一次构造代码块特性,我们可以做诸如统计创建对象的次数等功能
(3)普通代码块
- 普通代码块和构造代码块的区别是,构造代码块是在类中定义的,而普通代码块是在方法体中定义的。且普通代码块的执行顺序和书写顺序一致
- 缩短了生命周期,及早释放资源
5.执行顺序
- 静态代码块>构造代码块>构造函数>普通代码块
- 父类和子类执行顺序: 对象的初始化顺序:
首先执行父类静态的内容,父类静态的内容执行完毕后,接着去执行子类的静态的内容,当子类的静态内容执行完毕之后,再去看父类有没有构造代码块,如果有就执行父类的构造代码块,父类的构造代码块执行完毕,接着执行父类的构造方法;父类的构造方法执行完毕之后,它接着去看子类有没有构造代码块,如果有就执行子类的构造代码块。子类的构造代码块执行完毕再去执行子类的构造方法。总之一句话,静态代码块内容先执行,接着执行父类构造代码块和构造方法,然后执行子类构造代码块和构造方法
实例展示
package com.my.test02; public class Text05 { public static void main(String[] args){ { System.out.println("普通代码块1"); } Sno s= new Sno("参数"); { System.out.println("普通代码块2"); } new Sno(); { System.out.println("普通代码块3"); } } } class Father{ String a="父类变量"; public Father(){ System.out.println("父类空参构造"); } { System.out.println("父类构造代码块"); } static{ System.out.println("父类静态代码块"); } } class Sno extends Father{ public Sno(){ System.out.println("子类无参构造"); } public Sno(String s){ System.out.println(s+" "+a+" 子类有参构造"); } { System.out.println("子类构造代码块"); } static{ System.out.println("子类静态代码块"); } }
运行结果
普通代码块1 //证明了普通代码块是按顺序执行
父类静态代码块 //证明了父类的静态代码块优先执行,但必须是在类加载时执行
子类静态代码块 //证明了父类的东西优先于子类执行
父类构造代码块 //证明了静态代码块优先于局部代码块
父类空参构造 //证明了父类构造方法优先于子类构造代码块
子类构造代码块
参数 父类变量 子类有参构造 //子类构造方法最后执行
普通代码块2 //证明了普通代码块是按顺序执行
父类构造代码块 //证明了类每被调用一次,父类的构造代码块也会跟着执行一次,而且证明了静态代码块只能执行一次
父类空参构造 //证明了父类构造方法也是子类每被调用一次同时调用一次,而且优先于子类的构造代码块执行
子类构造代码块
子类无参构造
普通代码块3
二、封装 指隐藏对象的属性和实现细节,仅对外提供公共访问方式
在面向对象程式设计方法中,封装(英语:Encapsulation)是指一种将抽象性函式接口的实现细节部份包装、隐藏起来的方法。
封装可以被认为是一个保护屏障,防止该类的代码和数据被外部类定义的代码随机访问。
要访问该类的代码和数据,必须通过严格的接口控制。
封装最主要的功能在于我们能修改自己的实现代码,而不用修改那些调用我们代码的程序片段。
适当的封装可以让程式码更容易理解与维护,也加强了程式码的安全性。
封装的优点
-
1. 良好的封装能够减少耦合。
-
2. 类内部的结构可以自由修改。
-
3. 可以对成员变量进行更精确的控制。
-
4. 隐藏信息,实现细节。
实例展示
package com.my.object; public class Myprivate { String name; private String sex; //将性别私有化只有本类中的方法可以访问 public Myprivate(){} public Myprivate(String name,String sex){ this.name=name; this.sex=sex; } public void print(){ System.out.println("name="+name+",sex="+sex); } }
三、继承 从已知的一个类中派生出新的一个类,叫子类。子类实现了父类所有非私有化属性和方法
继承的特性:
-
子类拥有父类非 private 的属性、方法。
-
子类可以拥有自己的属性和方法,即子类可以对父类进行扩展。
-
子类可以用自己的方式实现父类的方法。
-
Java 的继承是单继承,但是可以多重继承,单继承就是一个子类只能继承一个父类,多重继承就是,例如 A 类继承 B 类,B 类继承 C 类,所以按照关系就是 C 类是 B 类的父类,B 类是 A 类的父类,这是 Java 继承区别于 C++ 继承的一个特性。
-
提高了类之间的耦合性(继承的缺点,耦合度高就会造成代码之间的联系越紧密,代码独立性越差)。
规则:
- 子类继承父类非私有的所有属性和方法,不能继承父类的构造方法;
- 实例化子类对象的步骤:先执行父类的构造方法,再执行子类的构造方法;
重写定义:
- 子类重新声明从父类继承来的方法,称为方法重写;
- 方法重写时,方法的声明部分要和父类保持一致(返回值类型,方法名,参数);
- 重写方法的访问权限要大于等于父类中方法的访问权限;
- 子类重写父类方法,子类对象调用的是子类中重写后的方法;
- 使用static修饰的方法不能被重写,但是可以被子类重写声明;
- 不同包的子类可以重写父类中protected修饰的方法,但是不能以继承的形式,用子类对象直接调用父类的该方法;
Final关键字:
- 当final修饰类时,当前类不能被继承;
- 当final修饰方法时,该方法不能被重写;但是可以继承
- 当final修饰变量时,变量的值不能被修改,即为常量;
实例展示
package com.my.object; public class Myextends { public static void main(String[] args){ cat c=new cat("鱼"); dog d=new dog("骨头"); c.eat(); c.sleep(); //继承了父类的sleep方法 d.eat(); d.sleep(); //继承了父类的sleep方法 } } class Animal{ //父类 public void sleep(){ System.out.println("sleep"); } } class cat extends Animal{ //猫类继承动物类 public String food; public cat(){} public cat(String food){ this.food=food; } public void eat(){ System.out.println("猫吃"+food); } } class dog extends Animal{ //狗类继承动物类 public String food2; public dog(){} public dog(String food2){ this.food2=food2; } public void eat(){ System.out.println("狗吃"+food2); } }
四、多态 多个不同的对象对同一消息做出响应,同一消息根据不同对象而采用各种不同的行为方法
多态的优点:
- 1. 消除类型之间的耦合关系
- 2. 可替换性
- 3. 可扩充性
- 4. 接口性
- 5. 灵活性
- 6. 简化性
用一个类都需要创建一个对象,有没有什么一种方法可以只创建一个对象,这就想到了多态,父类指向子类对象,指向谁父类就代表谁
实现的必要条件:
- 继承
- 重写
- 父类引用指向子类对象
instanceof关键字:检查对象是否属于某个类
语法:
if (对象名 instanceof 类名) {
类型转换代码;
}
实例展示
package com.my.object; public class Myextends { public static void main(String[] args){ Animal a1=new cat(); //父类指向猫类现在代表猫 Animal a2=new dog(); //父类指向狗类现在代表狗 a1.sleep(); //继承了父类的sleep方法 a1.eat(); //a1只能调用被子类重写过的方法和父类中的方法 a2.sleep(); a2.eat(); } } class Animal{ //父类 public void sleep(){ System.out.println("sleep"); } public void eat(){ System.out.println(); } } class cat extends Animal{ //猫类继承动物类 public void eat(){ //重写父类吃的方法 System.out.println("猫吃鱼"); } } class dog extends Animal{ //狗类继承动物类 public void eat(){ //重写父类吃的方法 System.out.println("狗吃肉"); } }
运行结果
sleep
猫吃鱼
sleep
狗吃肉
五、抽象 通过特定的实例抽取出共同的特征以后形成的概念的过程,它强调主要特征和忽略次要特征。
在面向对象的概念中,所有的对象都是通过类来描绘的,但是反过来,并不是所有的类都是用来描绘对象的,如果一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就是抽象类。
抽象类除了不能实例化对象之外,类的其它功能依然存在,成员变量、成员方法和构造方法的访问方式和普通类一样。
由于抽象类不能实例化对象,所以抽象类必须被继承,才能被使用。也是因为这个原因,通常在设计阶段决定要不要设计抽象类。
父类包含了子类集合的常见的方法,但是由于父类本身是抽象的,所以不能使用这些方法。
在Java中抽象类表示的是一种继承关系,一个类只能继承一个抽象类,而一个类却可以实现多个接口。
规则:
含有抽象方法的类,一定是抽象类;
- 抽象类中可以声明成员变量、常量、成员方法、抽象方法,抽象类中不一定要有抽象方法;
- 抽象类不能被实例化;
- 抽象类可以被继承;
- 可以通过两种方式获得抽象类对象:父类引用指向子类对象、匿名内部类;
- 子类必须重写抽象父类的所有抽象方法,或者是把子类也定义为抽象类;
- 如果一个类继承的抽象父类还有上级抽象父类,那么子类中需要要重写所有抽象父类的所有抽象方法;
- 抽象类也可以继承非抽象类,同时继承了父类的所有非私有的属性和方法;
实例展示
package com.my.object; public class Absract { public static void main(String[] args){ } } abstract class Office{ //抽象父类 public abstract void print(); //抽象方法不能被实例化 } class Word extends Office{ //Word类继承Office类 public void print(){ //必须重写父类定义为抽象的方法 System.out.println("print"); } } class Excel extends Office{ //Excel类继承Office类 public void print(){ //必须重写父类定义为抽象的方法 System.out.println("print2"); } }
六、接口
接口(英文:Interface),在JAVA编程语言中是一个抽象类型,是抽象方法的集合,接口通常以interface来声明。一个类通过继承接口的方式,从而来继承接口的抽象方法。
接口并不是类,编写接口的方式和类很相似,但是它们属于不同的概念。类描述对象的属性和方法。接口则包含类要实现的方法。
除非实现接口的类是抽象类,否则该类要定义接口中的所有方法。
接口无法被实例化,但是可以被实现。一个实现接口的类,必须实现接口内所描述的所有方法,否则就必须声明为抽象类。另外,在 Java 中,接口类型可用来声明一个变量,他们可以成为一个空指针,或是被绑定在一个以此接口实现的对象。
规则:
- 接口使用interface关键字修饰;
- 接口是一个完全抽象的抽象类;
- 接口中没有构造方法;
- 接口不能被实例化对象;
- 接口中可以声明静态常量、抽象方法、静态方法;
- 接口中不能声明实例方法,声明抽象方法时,不能使用static关键字修饰;
- 声明接口语句中,默认含有abstract关键字,抽象方法中也默认含有abstract关键字;
- 接口可以被实现,使用implements关键字,一个类实现一个接口,必须重写该接口中所有的抽象方法;
- 一个类可以实现多个接口,每个接口名用英文的逗号隔开,该类中必须重写所有已实现接口中的抽象方法;
- 接口可以继承接口,接口与接口间是多继承关系,接口不能继承类;
接口与类相似点:
- 一个接口可以有多个方法。
- 接口文件保存在 .java 结尾的文件中,文件名使用接口名。
- 接口的字节码文件保存在 .class 结尾的文件中。
- 接口相应的字节码文件必须在与包名称相匹配的目录结构中。
接口与类的区别:
- 接口不能用于实例化对象。
- 接口没有构造方法。
- 接口中所有的方法必须是抽象方法。
- 接口不能包含成员变量,除了 static 和 final 变量。
- 接口不是被类继承了,而是要被类实现。
- 接口支持多继承。
接口特性
- 接口中每一个方法也是隐式抽象的,接口中的方法会被隐式的指定为 public abstract(只能是 public abstract,其他修饰符都会报错)。
- 接口中可以含有变量,但是接口中的变量会被隐式的指定为 public static final 变量(并且只能是 public,用 private 修饰会报编译错误)。
- 接口中的方法是不能在接口中实现的,只能由实现接口的类来实现接口中的方法。
抽象类和接口的区别
- 1. 抽象类中的方法可以有方法体,就是能实现方法的具体功能,但是接口中的方法不行。
- 2. 抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是 public static final 类型的。
- 3. 接口中不能含有静态代码块以及静态方法(用 static 修饰的方法),而抽象类是可以有静态代码块和静态方法。
- 4. 一个类只能继承一个抽象类,而一个类却可以实现多个接口。
实例展示
package com.my.object; public interface Myinterface { int NUM=1; //抽象类中只有静态常量,且常量名字必须全部用大写字母 void print(); static void show(){ //抽象类中可以写静态成员方法 System.out.println("show"); } } class Wps implements Myinterface{ //Wps类实现Myinterface接口 public void print(){ //必须重写抽象类中除静态方法以外的所有方法 System.out.println("print"); } } class Word implements Myinterface{ //Word类实现Myinterface接口 public void print(){ //必须重写抽象类中除静态方法以外的所有方法 System.out.println("print"); } } class Excel implements Myinterface{ //Excel类Word类实现Myinterface接口 public void print(){ //必须重写抽象类中除静态方法以外的所有方法 System.out.println("print2"); } }
七、内部类
成员内部类
成员内部类声明在类中,方法体、代码块之外。和成员变量、成员方法在同一级别。
语法:
public class outer{
public class inner{
}
}
实例展示
//实例化成员内部类: //先实例化外部类 Out o = new Out(); //使用外部类对象,再实例化内部 Out.Inner inner = o.new Inner(); public class Out { //成员变量 public int a = 1; //成员内部类 public class Inner{ public int a = 2; //内部类的成员方法 public void print(){ //执行内部类中的实例变量a System.out.println(a); //执行外部类的实例变量a System.out.println(Out.this.a); } } }
静态内部类
声明的位置参考成员内部类。
语法:
public class outer{
public static class inner{
}
}
实例展示
//实例化静态内部的对象: Out.Inner inner = new Out.Inner(); public class Out { public static int a = 1; public int b = 3; //静态内部类 public static class Inner{ public static int a = 2; public static void print(){ //执行静态内部的静态变量 System.out.println(a); //执行外部类的静态变量 System.out.println(Out.a); //执行外部类的实例变量 Out o = new Out(); System.out.println(o.b); } } }
局部内部类
声明在方法体或代码块内,作用域范围在方法体或代码块内。
实例展示
public class Out { public void method(){ //局部内部类 class Inner{ //局部内部类的成员方法 public void print(){ System.out.println("局部内部类"); } } //实例化局部内部类 Inner inner = new Inner(); inner.print(); } } //执行局部内部类的方法: Test类: public static void main(String[] args) { Out o = new Out(); //o.method();错误提示 }
匿名内部类
- 声明位置同局部内部类一样,前提条件:必须继承一个类或实现一个接口,匿名内部类的声明和实例化对象是同时进行的;
- 一般使用于获得抽象类或接口对象;
语法:
父类名/接口名 对象名 = new 父类名/接口名(){
//匿名内部类成员
};
实例展示
//父类 public class Father { } //匿名内部类: public class Out { public void method(){ //匿名内部类对象 Father f = new Father(){ }; } }