Java面向对象
面向对象概念
面向过程和面向对象
面向过程的思维模式
面向过程的思维模式是简单的线性思维。
思考问题首先陷入第一步做什么、第二步做什么的细节中。这种思维模式适合处理简单的事情,比如:上厕所。
如果面对复杂的事情,这种思维模式会陷入令人发疯的状态!比如:如何造神舟十号!
面向对象的思维模式
面向对象的思维模式说白了就是分类思维模式。
思考问题首先会解决问题需要哪些分类,然后对这些分 类进行单独思考。最后,才对某个分类下的细节进行面向过程的思索。
这样就可以形成很好的协作分工。比如:设计师分了10个类,然后将10个类交给了10个人分别进行详细 设计和编码!
显然,面向对象适合处理复杂的问题,适合处理需要多人协作的问题!
如果一个问题需要多人协作一起解决,那么你一定要用面向对象的方式来思考!
对于描述复杂的事物,为了从宏观上把握、从整体上合理分析,我们需要使用面向对象的思路来分析整 个系统。但是,具体到微观操作,仍然需要面向过程的思路去处理。
OOP
Java的编程语言是面向对象的,采用这种语言进行编程称为面向对象编程
(Object-Oriented Programming, OOP)。
什么是面向对象
面向对象编程的本质就是:以类的方式组织代码,以对象的组织(封装)数据。
-
抽象:
忽略一个主题中与当前目标无关的那些方面,以便更充分地注意与当前目标有关的方面。抽象并不打算了 解全部问题,而只是选择其中的一部分,暂时不用关注细节。
也就是将多个物体共同点归纳出来 -
三大特性:
-
封装:
是对象和类概念的主要特性。封装是把过程和数据包围起来,对数据的访 问只能通过指定的方式。
-
继承
是一种联结类的层次模型,并且允许和支持类的重用,它提供了一种明确表述共性的方法。
-
多态
多态性是指允许不同类的对象对同一 属性/方法 作出不同响应。
多态性语言具有灵活、抽象、行为共享、代码共享的优势,很好的解决了应用程序函数同名问题。
-
类与对象的关系
类是一种抽象的数据类型,它是对某一类事物整体描述/定义,但是并不能代表某一个具体的事物.
对象是抽象概念的具体实例
Student s = new Student(1L,"tom",20);
s.study();
Car c = new Car(1,"BWM",500000);
c.run();
对象s就是Student类的一个实例,对象c就是Car类的一个具体实例,能够使用的是具体实例,而不是类。
类 只是给对象的创建提供了一个参考的模板而已.
但是在java中,没有类就没有对象,然而类又是根据具体的功能需求,进行实际的分析,最终抽象出来的.
对象和引用的关系
引用 "指向" 对象
使用类类型、数组类型、接口类型声明出的变量,都可以指向对象,这种变量就是引用类型变量
简称引用。
在程序中,创建出对象后,直接使用并不方便,所以一般会用一个引用类型的变量去接收这个对象
这个就是 所说的引用指向对象.
对象的创建
new
使用new关键字创建对象
使用new关键字创建的时候,除了分配内存空间之外,
还会给 创建好的对象 进行默认的初始化以及对类中构造器的调用。
Student s = new Student();
1)为对象分配内存空间,将对象的实例变量自动初始化默认值。(实例变量的隐式赋 值)
2)如果代码中实例变量有显式赋值,那么就将之前的默认值覆盖掉。(之后可以通过例子看到这个现象)
3)调用构造器
4)把对象内存地址值赋值给变量。( = 号赋值操作)
构造器
类中的构造器也称为构造方法,是在进行创建对象的时候必须要调用的。
特点:
1.必须和类的名字相同
2.必须没有返回类型,也不能写void
作用:
1.使用new创建对象的时候 使用类的构造器
2.构造器中的代码执行后,可以给对象中的属性 初始化赋值
```java
public class Student{
private String name;
public Student(){
name = "tom";
}
}
```
构造器重载
除了无参构造器之外,很多时候我们还会使用有参构造器,在创建对象时候可以给属性赋值.
public class Student{
private String name;
public Student(){
name = "tom";
}
public Student(String name){
this.name = name;
}
}
构造器间的调用
使用this关键字,在一个构造器中可以调用另一个构造器的代码。
注意:this的这种用法不会产生新的对象,只是调用了构造器中的代码而已
一般情况下只有使用new关键字才会创建新对象。
public class Student{
private String name;
public Student(){
this();
}
public Student(String name){
this.name = name;
}
}
默认构造器
在java中,即使我们在编写类的时候没有写构造器,那么在编译之后也会自动的添加一个无参构造器,这个无参构造器也被称为默认的构造器。
内存分析
JAVA程序运行的内存分析
- 栈 stack:
- 每个线程私有,不能实现线程间的共享!
- 局部变量放置于栈中。
- 栈是由系统自动分配,速度快!栈是一个连续的内存空间!
- 堆 heap:
- 放置new出来的对象!
- 堆是一个不连续的内存空间,分配灵活,速度慢!
- 方法区(堆的特殊区域):
- 被所有线程共享!
- 用来存放程序中永远是不变或唯一的内容。
(类代码信息、静态变量、字符串常量)
this
this的含义
this在类中表示当前类将来创建出的对象
public class Student{
private String name;
public Student(){
System.out.println("this = "+this);
}
public static void main(String[] args){
Student s = new Student();
System.out.println("s = "+s);
}
}
this和s打印的结果是一样的,内存地址也是一样的
s 是 在对象的 外部 执行对象
this是 在对象的 内部 执行对象本身.
this并不是只有一个,每个对象中都有一个属于自己的this
this的作用
- 区分实例变量和局部变量
public class Student{
private String name;
public void setName(String name){
//this.name表示类中的属性name
this.name = name;
}
}
- 调用类中的其他方法
public class Student{
private String name;
public void setName(String name){
//this.name表示类中的属性name
this.name = name;
}
}
- 调用类中的其他构造器
注:this的这种用法,只能在构造器中使用
普通的方法是不能用的.并且这局调用的代码只能出现在构造器中的第一句
public class Student{
private String name;
public Student(){
//调用一个参数的构造器,并且参数的类型是String
this("tom");
}
public Student(String name){
this.name = name;
}
}
面向对象的三大特性
封装
含义
在定义一个对象的特性的时候,有必要决定这些特性的可见性,即哪些特性对外部是可见的,哪些特性 用于表示内部状态。
通常,应禁止直接访问一个对象中数据的实际表示,而应通过操作接口来访问,这称为信息隐藏。
步骤
-
使用private 修饰需要封装的成员变量。
-
提供一个公开的方法设置或者访问私有的属性
设置 set()方法,命名格式: set属性名(); 属性的首字母要大写
访问 get()方法,命名格式: get属性名(); 属性的首字母要大写
//对象能在类的外部"直接"访问
public class Student{
public String name;
public void println(){
System.out.println(this.name);
}
}
public class Test{
public static void main(String[] args){
Student s = new Student();
s.name = "tom";
}
}
通过 private 关键字可以将类中的数据隐藏起来
在 类的外部需要访问这些私有属性,那么可以在类中提供对于的get和set方法,以便让用户在类的外部间接的访问到私有属性
//set负责给属性赋值
//get负责返回属性的值
public class Student{
private String name;
public void setName(String name){
this.name = name;
}
public String getName(){
return this.name;
}
}
public class Test{
public static void main(String[] args){
Student s = new Student();
s.setName("tom");
System.out.println(s.getName());
}
}
意义
-
提高程序的安全性,保护数据。
(可以在set方法中增加if条件语句,安全判断) -
隐藏代码的实现细节
-
统一用户的调用接口
-
提高系统的可维护性
-
便于调用者调用。
良好的封装,便于修改内部代码,提高可维护性。
良好的封装,可进行数据完整性检测,保证数据的有效性。
继承
继承概述
继承本质:
对某一批类的抽象,从而实现对现实世界更好的建模。
继承的作用:
继承的本质在于抽象。
类是对对象的抽象,继承是对某一批类的抽象。
为了提高代码的复用性。
【注】JAVA中类只有单继承,没有多继承! 接口可以多继承!
继承语法
public class student extends Person{
}
-
继承是类和类之间的一种关系。除此之外,类和类之间的关系还有依赖、组合、聚合等。
-
继承关系的俩个类,一个为子类(派生类),一个为父类(基类)。
子类继承父类,使用关键字extends来 表示。 -
类和和父类之间,从意义上讲应该具有"is a"的关系.
-
类和类之间的继承是单继承
-
父类中的属性和方法可以被子类继承
子类中继承了父类中的属性和方法后,在子类中能不能直接使用这些属性和方法,
是和这些属性和方法原有的修饰符(public protected default private)相关的。
- 父类中的构造器是不能被子类继承的。
但是子类的构造器中,会隐式的调用父类中的无参构造器(默认使用 super关键字)。
Object
Ctrl + H 可以查看继承关系
java中的每一个类都是"直接" 或者 "间接"的继承了Object类.
在Object类中,提供了一些方法被子类继承,那么就意味着,在java中,任何一个对象都可以调用这些被继承 过来的方法。(因为Object是所以类的父类)
例如:toString方法、equals方法、getClass方法等
Super
子类继承父类之后,在子类中可以使用super 访问 或 调用 父类中的属性和方法。
- 访问父类中的属性
public class Person{
protected String name = "zs";
}
public class Student extends Person{
private String name = "lisi";
public void test(String name){
System.out.println(name); //形参nanme
System.out.println(this.name); //"lisi"
System.out.println(super.name); //"zs"
}
}
- 调用父类中的方法
public class Person{
public void print(){
System.out.println("Person");
}
}
public class Student extends Person{
public void print(){
System.out.println("Student");
}
public void test(){
print();
this.print();
super.print();
}
}
- 调用父类的构造器
public class Person{
}
public class Student extends Person{
//new子类对象时,子类构造器中会隐式的调用父类的无参构造器
public Student(){
//super();
}
}
不管是显式还是隐式的父类的构造器,super语句一定要出现在子类构造器中第一行代码。
所以this和super不可能同时使用它们调用构造器的功能,因为它们都要出现在第一行代码位置。
- 注意:
- 用super调用父类构造方法,必须是构造方法中的第一个语句。
- super只能出现在子类的方法或者子类的构造方法中。
- super 和 this 不能同时调用构造方法。
(因为this也是在构造方法的第一个语句)
-
super 和 this 的区别:
- 代表的事物不一样:
super:代表父类对象的引用空间。
this:代表所属方法的调用者对象本身。- 使用前提不一致:
super:只能在继承的条件下才能使用。
this:没有继承也可以使用。- 调用构造方法:
super:调用的父类的构造方法。
this:调用本类的构造方法。
方法重写
Alt + Insert @Override
1. 方法重写只存在于子类和父类(包括直接父类和间接父类)之间。在同一个类中方法只能被重载,不能被重写.
-
静态方法不能重写
父类的静态方法不能被子类重写为非静态方法;//编译出错
父类的非静态方法不能被子类重写为静态方法;//编译出错
子类可以定义与父类的静态方法同名的静态方法(但是这个不是覆盖)
私有方法不能被子类重写。
子类继承父类后,是不能直接访问父类中的私有方法的,那么就更谈不上 重写了。
public class Person{
private void run(){
}
}
//编译通过,但这不是重写,只是俩个类中分别有自己的私有方法
public class Student extends Person{
private void run(){
}
}
-
重写语法注意点:
- 方法名必须相同
- 参数列表必须相同
- 访问控制修饰符可以被扩大,但是不能被缩小: public protected default private
- 抛出异常类型的范围可以被缩小,但是不能被扩大 ClassNotFoundException ---> Exception
- 返回类型要么相同,要么子类重写后的方法返回类型
必须是父类方法返回类型的子类型
-
重写意义:
子类继承父类,继承了父类中的方法,但是父类中的方法并不一定能满足子类中的功能需要,所以子类中需 要把方法进行重写。
多态
概述
多态可以让我们不用关心某个对象到底是什么具体类型,就可以使用该对象的某些方法,从而实现更加灵活的编程,提高系统的可扩展性。
一个对象的实际类型(new 时的,后面跟的类)是确定的
但是 指向这个对象的 引用的类型,却可以是这对象实际类型的任意父类型。
- 一个父类引用 可以指向它的任何一个 子类对象:
Object o = new AnyClass();
Person p = null;
p = new Student();
p = new Teacher();
p = new Person();
- 调用子类重写的方法:
public class Person{
public void run(){
}
}
public class Student extends Person{
public void run(){
//重写run方法
}
}
//调用到的run方法,是Student中重写的run方法 main:
Person p = new Student();
p.run();
new子类对象给父类引用,利用父类引用调用a方法
如果a方法在子类中没有重写,那么就是调用的是子类继承父类的a方法,
如果子类中重写了,那么调用的就是重写之后的方法。
- 调用子类特有的方法:
public class Person{
public void run(){
}
}
public class Student extends Person{
public void go(){
}
}
//需要把变量p的类型进行转换
Person p = new Student();
Student s = (Student)p;
s.go();
或者
//注意这种形式前面必须要俩个小括号
((Student)p).go();
多态存在的条件
-
有继承关系
-
子类重写父类方法
以下父类中的三种类型的方法是没 有办法表现出多态特性的(因为不能被重写):
- static方法,因为被static修饰的方法是属于类的,而不是属于实例的
- final方法,因为被final修饰的方法无法被子类重写
- private方法和protected方法
前者是因为被private修饰的方法对子类不可见
后者是因为尽管被 protected修饰的方法可以被子类见到,也可以被子类重写
但是它是无法被外部所引用的,一个 不能被外部引用的方法,怎么能谈多态呢
-
父类引用指向子类对象
instanceof
- 编译是否通过
System.out.println(x instanceof Y);
该代码能否编译通过,主要是看 声明变量x的类型 和 Y 是否存在子父类的关系。
有子父类关系就编译通过, 没有子父类关系就是编译报错.
之后学习到的接口类型和这个是有点区别的。
- 输出结果 ture/false
System.out.println(x instanceof Y);
输出结果是true还是false,
变量 x所指向的 对象实际类型 是Y类型的"子类型"
输出ture
变量 x所指向的 对象实际类型 不是Y类型的"子类型"
输出false
Object o = new Person();
System.out.println(o instanceof Student);//false
System.out.println(o instanceof Person);//true
System.out.println(o instanceof Object);//true
System.out.println(o instanceof Teacher);//false
System.out.println(o instanceof String);//false
修饰符
static修饰符
修饰变量
在类中,使用static修饰的成员变量,就是静态变量,反之为非静态变量。
static不能修饰局部变量
静态变量和非静态变量的区别:
静态变量属于类的,"可以"使用类名来访问(也可以用对象访问)
非静态变量是属于对象的,"必须"使用对象来访问.
在加载类的过程中为静态变量分配内存,实例变量在创建对象时分配内存
所以静态变量可以使用类名来 直接访问,而不需要使用对象来访问.
public class Student{
private static int age;
private double score;
public static void main(String[] args) {
Student s = new Student();
//推荐使用类名访问静态成员
System.out.println(Student.age);
System.out.println(s.age);
System.out.println(s.score);
}
}
修饰方法
在类中,使用static修饰的成员方法,就是静态方法,反之为非静态方法。
静态方法和非静态方法的区别:
静态方法数属于类的,"可以"使用类名来调用(也可以用对象访问)
静态方法"不可以"直接访问类中的非静态变量和非静态方法
但是"可以"直接访问类中的静态变量和静态方法
父类的静态方法可以被子类继承,但是不能被子类重写
非静态方法是属于对象的,"必须"使用对象来调用
非静态方法"可以"直接访问类中的非静态变量和非静态方法
也"可以"直接访问类中的静态变量和静态方法
父类的非静态方法不能被子类重写为静态方法
static代码块
- 类中可以编写 代码块 和 静态代码块
public class Person {
{
//代码块(匿名代码块)
}
static{
//静态代码块
}
}
-
匿名代码块和静态代码块的执行:
匿名代码块是在创建对象的时候自动执行的,并且在构造器执行之前。
同时匿名代码块在每次创建对象的时候都会自动执行. 静态代码块是在类加载完成之后就自动执行,并且只执行一次
-
匿名代码块和静态代码块的作用
匿名代码块的作用是给对象的成员变量初始化赋值
但是因为构造器也能完成这项工作,所以匿名代码块 使用的并不多。 静态代码块的作用是给类中的静态成员变量初始化赋值
-
创建和初始化对象的过程
- 类加载,同时初始化类中静态的属性
- 执行静态代码块
- 分配内存空间,同时初始化非静态的属性(赋默认值)
- 调用Student的父类构造器
- 对Student中的属性进行显示赋值(如果有的话)
- 执行匿名代码块
- 执行构造器
- 返回内存地址
-
静态导入
静态导包就是java包的静态导入,用import static代替import静态导入包是JDK1.5中的新特性。 意思是导入这个类里的静态方法。
好处:这种方法的好处就是可以简化一些操作,例如打印操作System.out.println(…);就可以将其写入一 个静态方法print(…),在使用时直接print(…)就可以了。
但是这种方法建议在有很多重复调用的时候使用,如果仅有一到两次调用,不如直接写来的方便。
import static java.lang.Math.random;
import static java.lang.Math.PI;
public class Test {
public static void main(String[] args) {
//之前是需要Math.random()调用的
System.out.println(random()); System.out.println(PI);
}
}
final修饰符
修饰变量
用final修饰的变量表示常量,只能被赋一次值
其实使用final修饰的变量也就成了常量了,因为值不会再变了。
【修饰局部变量】
public class Person{
public void print(final int a){
//编译报错,不能再次赋值,传参的时候已经赋过了
a = 1;
}
}
public class Person{
public void print(){
final int a;
a = 1;
//编译报错,不能再次赋值
a = 2;
}
}
【修饰实例变量】
public class Person{
private final int a;
}
只有一次机会,可以给此变量a赋值的位置:
声明的同时赋值
匿名代码块中赋值
构造器中赋值(类中出现的所有构造器都要写)
【修饰静态变量】
public class Person{
private static final int a;
}
只有一次机会,可以给此变量a赋值的位置:
声明的同时赋值
静态代码块中赋值
【修饰引用变量】
main:
final Student s = new Student();
//编译通过,可以更改对象的属性
s.setName("tom");
s.setName("zs");
//编译报错,不能修改引用s指向的内存地址
s = new Student();
修饰方法
用final修饰的方法可以被继承,但是**不能被子类重写**。
例如:每个类都是Object类的子类,继承了Object中的众多方法,在子类中可以重写toString方法、equals方法等
但是不能重写getClass方法、wait方法等,因为这些方法都是使用fianl修饰的。
我们也可以定义final修饰的方法:
public class Person{
public final void print(){
}
}
public class Student extends Person{
//编译报错,不能重写父类中被final修饰的方法
public void print(){
}
}
修饰类
用final修饰的类**不能被继承**,没有子类。
例如:我们是无法写一个类去继承String类
我们也可以定义final修饰的类:
public final class Action{
}
//编译报错
public class Go extends Action{
}
abstract修饰符 --> 抽象类
抽象类
abstract修饰符
如果修饰方法,那么该方法就是抽象方法;
如果修饰类,那 么该类就是抽象类。
抽象类和抽象方法的关系
抽象类中可以没有抽象方法
但是有抽象方法的类一定要声明为抽象类
语法
public abstract class Action{
//声明方法的时候,加上abstract修饰符,并且去掉方法的大口号,同时结尾加上分号,该方法就是抽象方法。
public abstract void doSomething();
}
//这是一个普通的
public void doSomething(){
...
}
特点
抽象类:
不能new对象,只能靠 让子类继承 的。
抽象类中可以有普通方法
抽象方法:
只有方法的声明,没有方法的实现,它是用来 让子类实现 的。
注:子类继承抽象类后,需要实现抽象类中没有实现的抽象方法
否则这个子类也要声明为抽象类
作用
抽象类和抽象方法起到一个框架作用。很方便后期的调用和重写
抽象方法是为了程序的可扩展性。重写抽象方法时即可实现同名方法但又非同目的的要求。
接口
概述
接口就是比“抽象类”还“抽象”的“抽象类”,可以更加规范的对子类进行约束。
全面地专业地实现了: 规范和具体实现的分离。
接口中所有方法都是抽象方法。
接口是完全面向规范的,规定了一批类具有的公共方法规范。
接口是两个模块之间通信的标准,通信的规范。如果能把你要设计的系统之间模块之间的接口定义 好,就相当于完成了系统的设计大纲,剩下的就是添砖加瓦的具体实现了。
接口和抽象类的区别
声明:抽象类 abstract class
接口 interface
继承:抽象类是用来被继承的,java中的类是单继承。
类A继承了抽象类B,那么类A的对象就属于B类型了,可以使用多态 一个父类的引用,可以指向这个父类的任意子类对象
注:继承的关键字是extends
接口是用来被类实现的,java中的接口可以多继承后再实现。
类A实现接口B、C、D、E..,那么类A的对象就属于B、C、D、E等类型了,可以使用多态 一个接口的引用,可以指向这个接口的任意实现类对象
注:实现的关键字是implements
接口中的方法都是抽象方法
public interface Action{
public abstract void run();
//默认就是public abstract修饰的
void test();
public void go();
}
接口中的变量都是静态常量(pubilc static final修饰)
接口中可以不写任何属性,但如果写属性了,该属性静态常量。
注:可以直接使用 接口名 访问其属性。因为是public static修饰的
注:声明的同时就必须赋值.(因为接口中不能编写静态代码块)
public interface Action{
public static final String NAME = "tom";
//默认就是public static final修饰的
int AGE = 20;
}
main:
System.out.println(Action.NAME); System.out.println(Action.AGE);
一个(实现)类可以实现多个接口
public class Student implements A,B,C,D{
//Student需要实现接口A B C D中所有的抽象方法
//否则Student类就要声明为抽象类,因为有抽象方法没实现
}
main:
A s1 = new Student(); //s1只能调用接口A中声明的方法以及Object中的方法
B s2 = new Student(); //s2只能调用接口B中声明的方法以及Object中的方法
C s3 = new Student(); //s3只能调用接口C中声明的方法以及Object中的方法
D s4 = new Student(); //s4只能调用接口D中声明的方法以及Object中的方法
一个接口可以继承多个父接口
public interface A{
public void testA();
}
public interface B{
public void testB();
}
//接口C把接口A B中的方法都继承过来了
public interface C extends A,B{
public void testC();
}
//Student相当于实现了A B C三个接口,需要实现所有的抽象方法
//Student的对象也就同时属于A类型 B类型 C类型
public class Student implements C{
public viod testA(){}
public viod testB(){}
public viod testC(){}
}
main:
C o = new Student();
System.out.println(o instanceof A);//true
System.out.println(o instanceof B);//true
System.out.println(o instanceof C);//true
System.out.println(o instanceof Student);//true
System.out.println(o instanceof Object);//true
System.out.println(o instanceof Teacher);//false
//编译报错
System.out.println(o instanceof String);
System.out.println(o instanceof X);
如果o是一个接口类型声明的变量,那么只要X不是一个final修饰的类,该代码就能通过编译,
结果为ture:变量o指向的 对象的实际类型,是X的 子类 或者 实现类。
结果为false:变量o指向的 对象的实际类型,不是是X的 子类 或者 实现类。
接口的作用
- Java接口中的成员变量默认都是public,static,final类型的(都可省略),必须被显示初始化,即接口中的成员变量为常量(大写,单词之间用"_"分隔)
- Java接口中的方法只能是抽象方法(默认是public,abstract类型的(都可省略)),没有方法体,不能被实例化
- 接口中没有构造方法,接口不能new对象(被实例化)
- 一个接口不能实现(implements)另一个接口,但它可以继承多个其它的接口
- Java接口必须通过类来实现它的抽象方法,它必须实现接口中的所有抽象方法,否则这个类必须声明为抽象类
- 一个类实现多个接口,间接的实现了多继承.
内部类
概述
内部类就是在一个类的内部在定义一个类
比如,A类中定义一个B类,那么B类相对A类来说就称为内部类,而A类相对B类来说就是外部类了。
内部类分为四种:
-
成员内部类
-
静态内部类
-
局部内部类
-
匿名内部类
实例内部类(非静态内部类)
注:实例内部类中不能写静态属性和方法
- 要想访问实例内部类中的内容,必须通过外部类对象来实例化内部类。
public class Outer {
private int id;
public void out(){
System.out.println("这是外部类方法");
}
class Inner{
public void in(){
System.out.println("这是内部类方法");
}
}
}
public class Test{
public static void main(String[] args) {
//实例化成员内部类分两步
//1、实例化外部类
Outer outObject = new Outer();
//2、通过外部类调用内部类
Outer.Inner inObject = outObject.new Inner();
//测试,调用内部类中的方法
inObject.in();//打印:这是内部类方法
}
}
- 实例内部类能够访问外部类所有的属性和方法
原理就是在通过外部类对象实例化内部类对象时,外部类对象 把自己的引用传进了内部类
public class Outer {
private int id;
public void out(){
System.out.println("这是外部类方法");
}
class Inner{
public void in(){
System.out.println("这是内部类方法");
}
//内部类访问外部类私有的成员变量
public void useId(){
System.out.println(id+3);。
}
//内部类访问外部类的方法
public void useOut(){
out();
}
}
}
public class Test{
public static void main(String[] args) {
//实例化成员内部类分两步
//1、实例化外部类
Outer outObject = new Outer();
//2、通过外部类调用内部类
Outer.Inner inObject = outObject.new Inner();
//测试
inObject.useId();//打印3,因为id初始化值为0,0+3就为3,其中在内部类就使用了 外部类的私有成员变量id。
inObject.useOut();//打印:这是外部类方法
}
}
- 如果实例内部类中的变量名和外部类的成员变量名一样
要通过通过 Outer.this. 访问外部类的属性和方法
通过 this. 访问内部类成员属性
public class Outer {
private int id;//默认初始化0
public void out(){
System.out.println("这是外部类方法");
}
class Inner{
private int id=8; //这个id跟外部类的属性id名称一样。
public void in(){
System.out.println("这是内部类方法");
}
public void test(){
System.out.println(id);//输出8,内部类中的变量会暂时将外部类的成员 变量给隐藏
/*如何调用外部类的成员变量:
通过Outer.this,想实例化内部类对象,就必须通过外部类对象,当外部类对象来new出内部类对象时,会
把自己(外部类对象)的引用传到了内部类中,所以内部类就可以通过Outer.this来访问外部类的属性和方法
这里由于有两个相同的属性名称,所以需要显示的用Outer.this来调用外部类的属性,平常如果属性名不重复,那么我们在内部类中调用外部类的属性和方法时,前面就隐式的调用了Outer.this。
*/
System.out.println(Outer.this.id);//输出外部类的属性id。也 就是输出0
}
}
}
静态内部类
使用static修饰的 内部类 就叫静态内部类。
注意:
- 静态内部类能够直接被外部类给实例化,不需要使用外部类对象
- 静态内部类中,不能访问非静态的变量或方法(自身或者外部类的属变量或方法都不行)
- 静态内部类中可以声明非静态的变量或方法,也可以声明静态的变量或方法
但非静态内部类中不可以声明静态的变量或方法
public class StaticInnerClassTest {
private String name;
private static int age;
public void run(){}
public static void go(){}
//外部类访问静态内部类
public void test(){
StaticInnerClass sic = new StaticInnerClass();
//静态的内部类不需要依赖 外部类,所以不用this
sic.name = "tom";
sic.test1("jack");
StaticInnerClass.age=10;
StaticInnerClass.test2("xixi");
}
private static class StaticInnerClass{
private String name;
private static int age;
public void test1(String name){
System.out.println(name);
System.out.println(this.name);
System.out.println(StaticInnerClass.age);
System.out.println(StaticInnerClassTest.age);
//System.out.println(StaticInnerClassTest.this.name);静态类不能访 问非静态属性
StaticInnerClassTest.go();
//StaticInnerClassTest.this.run();静态类不能访问非静态方法
}
public static void test2(String name){
//只能访问自己和外部类的静态属性和方法
System.out.println(name);
//System.out.println(this.name);静态方法里面连自己类的非静态属性都不能 访问
System.out.println(StaticInnerClass.age);
System.out.println(StaticInnerClassTest.age);
//System.out.println(StaticInnerClassTest.this.name);静态方法不能 访问非静态属性
StaticInnerClassTest.go();
//StaticInnerClassTest.this.run();静态方法不能访问非静态方法
}
}
}
局部内部类
局部内部类是在一个方法内部声明的一个类
-
局部内部类中可以访问外部类的成员变量及方法
局部内部类中可以定义非静态变量,不能定义静态变量。 -
局部内部类中如果要访问该内部类所在方法中的局部变量
那么这个局部变量就必须是final修饰的
public class Outer {
private int id;
//在method01方法中有一个Inner内部类,这个内部类就称为局部内部类
public void method01(){
final int cid = 3; //这个就是局部变量cid。要让局部内部类使用,就得变为 final并且赋值,如果不使用final修饰,就会报错
class Inner{
public void in(){
System.out.println("这是局部内部类");
}
//内部类中的使用局部变量cid的方法
public void useCid(){
System.out.println(cid);
}
}
}
}
原因:
final修饰变量:变为常量,会在常量池中放着
如果不实用final修饰,当局部内 部类被实例化后,方法弹栈,局部变量随着跟着消失,这个时候局部内部类对象在想去调用该局部变 量,就会报错,
因为该局部变量已经没了,当局部变量用fanal修饰后,就会将其加入常量池中,即使方 法弹栈了,该局部变量还在常量池中呆着,局部内部类也就是够调用。
所以局部内部类想要调用局部变 量时,需要使用final修饰,不使用,编译度通不过。
- 调用局部内部类中的方法
局部内部类只能在自己的方法中用
因为局部内部类相当于一个局部变量,出了方法就找不到了。
局部内部类不能通过外部类对象直接实例化,而是在方法中实例化出自己来
然后通过 引用调用自己类 中的方法。
public class Outer {
private int id;
public void out(){
System.out.println("外部类方法");
}
public void method01(){
class Inner{
public void in(){
System.out.println("这是局部内部类");
}
}
//关键在这里,如需要在method01方法中自己创建内部类实例,然后调用内部类中的方法,等待 外部类调用method01方法,就可以执行到内部类中的方法了。
Inner In = new Inner();
In.in();
}
}
匿名内部类
- 匿名对象
public class Test {
public static void main(String[] args) {
//只需要用一次
apple.eat();
//这种就叫做匿名对象的使用,不把实例保存到变量中。
new Apple().eat();
}
}
class Apple{
public void eat(){
System.out.println("我要被吃了");
}
}
- 匿名内部类
只需要用一次,不需要在类中先定义一个内部类
等待需要用的时候,就临时实现这个内部类
因为用次数少,可能就这一次,那么这样写内部类,更方便。
注意点:
- 匿名内部类需要依托于其他类或者接口来创建
但不能指定继承或者实现其他类或接口
也不能被其他类所继承 - 匿名内部类的声明必须是在使用new关键字的时候
- 匿名内部中,我们不能写出其构造器,因为没有名字。
- 匿名内部中,除了重写上面的方法外,一般不会再写其他独有的方法,因为从外部不能直接调用到。(间接是调用到的)
【不使用匿名内部类实现接口】
public class Test {
public static void main(String[] args) {
/*如果我们需要使用接口中的方法,我们就需要走3步,
1、实现接口
2、创建实现接口类的实 例对象
3、通过对象调用方法
*/
//第二步
Test02 test = new Test02();
//第三步
test.method();
}
}
//接口Test1
interface Test01{
public void method();
}
//第一步、实现Test01接口
class Test02 implements Test01{
@Override
public void method() {
System.out.println("实现了Test接口的方法");
}
}
【使用匿名内部类实现接口】
public class Test {
public static void main(String[] args) {
//如果我们需要使用接口中的方法,我们只需要走一步,就是使用匿名内部类,直接将其 类的对象创建出来。
new Test1(){
public void method(){
System.out.println("实现了Test接口的方法"); }
}.method();
}
}
interface Test1{
public void method();
}
解析:
new Test1(){...}这个的作用就是将接口给实现了,只不过这里实现该接口的是一个匿名类,只能使用这一次,
将这个类new出来,就能获得一个实现了Test1接口的类的实例对象
通过该实例对象,就能调用该类中的方法了
浙公网安备 33010602011771号