java 学习笔记2
1.面向对象编程
1.1 初步
Object-orientation
八字:
组织代码,分装数据
所以面向对象是以类的方式组织代码,以对象的方式封装数据。
面向过程:以方法为核心。
软件危机。
所以之后将变量放在结构体中。
然后发现有些方法经常调用一些变量。
方法都放入类似结构体的结构中。也就是类。
所以将变量和方法封装到类中。
最后就以类为单位调用。
软件足够复杂,所以面向对象很方便。
类似公司人多了,增加部门。
物以类聚。相近的部分自动聚合在一起。
开车过程:
面向过程(线性思维):
1. 采离合
2. 挂挡
3. 踩油门,放离合
4. 开了
面向对象(适合多角色协作):
1. 驾驶员
2. 汽车
3. 驾驶员开汽车car.start();
造车:
面向过程:
一步一步来做。
面向过程:将要造的东西列出,完成,组装。
所以面向对象编程OOP
的本质是以类的方式组织代码,以对象的方式封装数据。
面向对象思维:OOA,OOD面向对象分析和设计。
1.2 对象和类的关系
对象:具体的事物。
类:对对象的抽象abstract
。
抽象:提取的意思。抽出像的部分。
数学抽象,就是提取数量的概念,不是难的意思。
class类:分类的概念。
现有具体的对象,然后抽象各个对象之间象的部分,归纳出类通过类再认识其他对象。
从设计考虑,先有对象再有类。
从编程角度考虑,先写类再组织对象。
设计考虑:
假如:构建天使类。
抽象她们的共同部分:
1. 带翅膀
2. 女孩
3. 善良
对象和类的关系:特殊到一般,具体到抽象。
类:class
对象: Object, instance(实例)。
类可以看成对象的模板,对象可以看作该类的一个具体实例。
package cn.bjsxt.oop;
public class Student {
// 静态的数据
String name;
int id;
int age;
String gender;
int weight;
// 动态的行为
public void study() {
System.out.println(name + "在学习");
}
public void sayHello(String sname) {
System.out.println(sname + "向" + name + "说:你好");
}
public static void main(String[] args) {
Student s1 = new Student();
s1.name = "zhangsan";
s1.study();
s1.sayHello("xiaohuozi");
Student s2 = new Student();
s2.age = 18;
s2.name = "Lisi";
}
}
以类的方式组织代码,以对象的方式组织/封装数据。
对象s1。对象以类为模板。以对象组织数据。
s1:
name:”zhangsan”;
id;
age;
gender;
weight;
stduy();
sayHello(“xiaohuozi”);
1.3 程序执行内存分析
引用类型:reference。
JAVA语言中除基本类型之外的变量都称之为引用类型。
属性和方法构成。
属性:
① 属性默认初始化
没赋值之前,默认值。
引用类型都是null。例如String。
基本类型都是0。
基本数字类型都是0或0.0。
boolean:false。
char:\u0000。
② 方法
JAVA中方法参数传递都是值传递。
③ 局部变量需要自己初始化。
1.3.1 内存分析 1:
概念:
栈(自动分配连续的空间,后进先出)存放局部变量。
堆(不连续) 存放new出来的对象。
方法区(堆的一部分)存放类的代码信息、static变量、常量池(字符串常量)等。
// 第一行
// Student
// ① JVM首先会在内存方法区中找是否有Student这个类。
// ② 有则通过类加载器ClassLoader加载Student类。
// ③ 加载完成之后,在方法区中就有Student类的信息(代码(属性、方法),static变量,常量池(字符串常量))。
// ④ 创建s1 存放在栈。
// ⑤ new Student()调用构造器,对象放入堆中。堆方法指向方法区的类方法
// ⑥ "=" 把新构造的类的首地址给s1
Student s1 = new Student();
// 第二行
// ① 先栈中找s1,找到转到堆中,找到name属性
// ② 创建一个"zhangsan"的字符串常量在方法区。
// ③ 把s1的name属性指向② 中创建的字符串常量,即存放地址。
// 操作对象都是操作地址
s1.name = "zhangsan";
// 第三行
// 开启一个栈帧,stackframe,去调用方法区的地址
s1.study();
// 开辟一个栈帧,将字符串传递给sname,
s1.sayHello("xiaohuozi");
// 已经有Student,不用再次加载,直接用。只执行④ ⑤ ⑥。
Student s2 = new Student();
// 基本数据类型18,直接赋值
s2.age = 18;
// 和上面类似
s2.name = "Lisi";
见下图:
1.3.2 内存分析2
// 首先再创建一个类Computer
public class Computer {
String brand;
int cpuSpeed;
}
// 学生类增加一个属性
Computer computer;
对以下代码进行内存分析
新建的Test2类。
新建的Test2类也会加载到方法区
Student s1 = new Student();
s1.name = "zhangsan";
s1.age = 18;
// 常量池有个共享机制
// 加载Computer类到方法区(代码,static变量,常量池)。
// 构造新类。之后将地址给c。
Computer c = new Computer();
// 把"dell"的地址给c.brand。
c.brand = "dell";
// 直接赋值为100
c.cpuSpeed = 100;
// 把c的地址给s1.computer
s1.computer = c;
System.out.println(s1.computer.brand);
// 把c的值换了,所以s1.computer.brand也改变。
c.brand = "联想";
System.out.println(s1.computer.brand);
1.4 垃圾回收机制
Garbage Collection
C++如何回收对象,由程序员负责。
JAVA中通过垃圾回收机制来执行。
对象没有引用,则算作垃圾。
对象空间释放直接赋值null。
- 程序员无权调用垃圾回收器。
- 程序员可以通过System.gc()。通知GC运行,但是JaVA规范并不能保证立刻运行。只是一个建议,不一定执行。
- finalize方法,是JAVA提供给程序员释放对象或资源的方法,但是尽量少用。
C++ 灵活。
JAVA 统一。
餐馆举例:
程序员 - 食客。
编程语言 - 餐馆。
C++:
食客吃完饭,全部打理一遍才能离开,但是不可能所有食客都素质高(编程水平高)。当垃圾过多之后,软件就崩溃了
JAVA:
餐馆雇一个服务员来进行进行垃圾回收,食客吃完饭,不用管,服务员进行打理。
1.5 构造器
补充说一下:
Computer c = new Computer();
c.brand = "dell";
String str1 = "dell";
System.out.println(str1 == c.brand);
// true
内存图走一下,可以发现相等。
又称为构造方法。Constructor
(其实就是C++里面的构造方法。)
构造器用于构造该类的实例。
格式如下:
[修饰符] 类名 (形参列表) {
语句
}
需要注意
① 通过new关键字调用
② 构造器不用写返回类型。实际上是有返回值的。
③ 如果我们没有定义构造器,则系统会自动定义一个无参的构造函数。如果定义则编译器不会添加。
④ 构造器的方法名必须和类名一致。
⑤ 作用:构造该类的对象,经常也用来初始化对象的属性。
定义一个点类,表示三维空间中的点。
- 可以生成具体特定坐标的点对象
- 提供可以设置三个坐标的方法
提供可以计算该”点”距其他点距离的方法。
public class Point {
double x, y, z;
// 通过构造器初始化对象的属性
public Point(double _x, double _y, double _z) {
x = _x;
y = _y;
z = _z;
}
public void setX(double _x) {
x = _x;
}
public void setY(double _y) {
y = _y;
}
public void setZ(double _z) {
z = _z;
}
public double distance(Point p) {
double result = Math.sqrt((x - p.x) * (x - p.x) + (y - p.y) * (y - p.y) + (z - p.z) * (z - p.z));
return result;
}
public static void main(String[] args) {
Point p = new Point(3, 4, 8);
Point p2 = new Point(200, 40, 80);
p.setX(10);
System.out.println(p.x);
System.out.println(p2.distance(p));
}
}
1.6 重载
overload
一个类中可以定义有相同的名字,但参数不同的多个方法。
调用时,会根据不同的参数表选择对应的方法。
两同三不同
- 同一个类,同一个方法名。
- 不同:参数列表不同(个数不同,类型不同,顺序不同)
返回值类型不同不构成重载,形参名称不同也不构成重载
其实顺序不同和类型不同类似。
构造方法也可以重载
只要注意一个即可:
不影响调用。没有歧义
public class TestOverload {
public static void main(String[] args) {
MyMath m = new MyMath();
int result = m.add(4, 5);
int result2 = m.add(4, 5, 8);
int result3 = m.add(4.8, 4);
System.out.println(result);
System.out.println(result2);
}
}
class MyMath {
int a;
int b;
public MyMath() {
}
public MyMath(int a) {
this.a = a;
}
public MyMath(int a, int b) {
this.a = a;
this.b = b;
}
// 初始方法
public int add(int a, int b) {
return a + b;
}
// 参数个数不同
public int add(int a, int b, int c) {
return a + b + c;
}
/*
* 返回值类型不同不构成重载
* public double add(double a, int b) { return a + b; }
*/
// 参数类型不同
public int add(double a, int b) {
return (int) (a + b);
}
// 参数顺序不同
public int add(int b, double a) {
return (int) (b + a);
}
}
1.7 static 关键字
在类中用static声明的成员变量称为静态变量或者叫做:类属性,类变量。
从属于类。即创建对象时候,没有staic变量。不放置在堆中,而是直接放置在方法区中。
所以可以直接进行赋值,调用等操作,而不用新建对象再调用。
用static声明的方法为静态方法,或者叫类方法。
新建类Student类
public class Student {
String name;
int id;
static int ss;
public static void printSS() {
// 如果是这样会报错
// name = "zhangsan";
// 静态方法不能引用非静态属性。
// 看下面的内存图。有一个先后关系。
// printSS()从属于类。name从属于对象。
// study()也是如此。
System.out.println(ss);
}
public void study() {
// 但反过来调用printSS()可以。
printSS();
System.out.println(name + "在学习");
}
public void sayHello(String sname) {
System.out.println(sname + "向" + name + "说:你好");
}
}
新建测试类Test类
public class Test {
public static void main(String[] args) {
// 可以直接调用
Student.ss = 323;
Student.printSS();
// 对象模板中没有static变量
Student s1 = new Student();
}
}
其实一句话:
类使用类的,对象既可以使用类的,也可以使用对象的。
内存分析见下图:
1.8 this关键字
- 普通方法中,this指代的是当前对象。
- 构造方法中,this总是指向正要初始化的对象。
- this()调用其他构造方法。
this不能用于static方法。
static从属于类。
代码:
package cn.bjsxt.oop.testThis;
public class Student {
String name;
int id;
// 构造方法中用的多一些
public Student(String name, int id) {
this(); // 通过this调用其他构造方法
// 只能放在第一句.放置在其他会提示:
// construcor call must be the first statement in a constructor
this.name = name;
this.id = id;
}
// 普通方法调用时候,默认传递了隐式参数this
// super也会传递
public void study() {
this.name = "zhangsan";
System.out.println(name + "在学习");
}
public void sayHello(String sname) {
System.out.println(sname + "向" + name + "说:你好");
}
}
1.9 继承
1.9.1 特点1
extends 扩展。子类是父类的扩展
面向对象的三大特征:继承、封装/隐藏、多态。
优点:
OOD:面向对象设计
类是对对象的抽象,继承是某一批类的抽象,从而实现对现实世界更好的建模。
OOP:面向对象编程
提高代码的复用性
抽象抽出像的部分。
// 如果没有继承
public class Animal {
String eye;
public void run() {
System.out.println("跑!");
}
public void eat() {
System.out.println("吃!");
}
}
class Mammal {
String eye;
public void run() {
System.out.println("跑!");
}
public void eat() {
System.out.println("吃!");
}
public void taisheng() {
System.out.println("我是胎生");
}
}
class Bird {
String eye;
public void run() {
System.out.println("飞!");
}
public void eat() {
System.out.println("吃!");
}
public void eggSheng() {
System.out.println("卵生");
}
}
// 使用继承之后,代码量大大减少
public class Animal {
String eye;
public void run() {
System.out.println("跑!");
}
public void eat() {
System.out.println("吃!");
}
public void sleep() {
System.out.println("睡!");
}
}
class Mammal extends Animal {
public void taisheng() {
System.out.println("我是胎生");
}
}
class Bird extends Animal{
// 重写,覆盖父类
public void run() {
System.out.println("飞!");
}
public void eggSheng() {
System.out.println("卵生");
}
}
总结:
- 子类继承父类,可以得到父类的全部属性和方法(除了父类的构造方法)
- 只有单继承,没有C++那种的多继承,
多继承:多个父类。类多之后不利用分析整体类的关系。
单继承:一个类最多只有一个直接父类。
- JAVA中的多继承可以通过接口来实现
- 如果定义一个类,没有调用extends,则它的父类是:java.lang.Object
- 不同的叫法:超类、父类、基类、子类、派生类
1.9.2 特点2
方法的重写override,和重载overload无关。
- 在子类中可以根据需要对从基类中继承来的方法进行重写。
- 重写方法必须和被重写方法具有相同方法名称、参数列表和放回类型
- 重写方法不能使用比被重写方法更严格的访问权限。(多态的原因)
1.9.3 Object类
- Object类是所有JAVA类的根基类
- 如果在类的声明中未使用extends关键字指明其基类,则默认基类为Object类
Object
Class Object is the root of the class hierarchy. Every class has Object as a superclass. All objects, including arrays, implement the methods of this class.
补充说一下,如何在Eclipse查看JDK源码。按住Ctrl键,点击跳转。
如果跳转页面没有,则设置external file的目录为JDK目录下的src.zip文件。
参考网页
指针指到类上,Ctrl + t可以查看该类的父类。查看层次。
右键选择open type hierarchy查看层次结构。
1.9.4 super关键字
super是直接父类对象的引用。可以通过super来访问父类中被子类覆盖的方法或属性。
普通方法中:
有两个隐式参数:this和super。
没有顺序限制。
构造方法中:
第一句中一定是super()。只能位于第一句。
内存图见下方:
1.9.5 继承VS组合
组合可以实现代码复用。
// 测试组合
public class Animal2 {
String eye;
public void run() {
System.out.println("跑!");
}
public void eat() {
System.out.println("吃!");
}
public void sleep() {
System.out.println("睡!");
}
public static void main(String[] args) {
Bird2 b = new Bird2();
b.run();
// 组合
b.animal2.eat();
}
}
class Mammal2 {
// 组合
Animal2 animal2 = new Animal2();
public void taisheng() {
System.out.println("我是胎生");
}
}
class Bird2{
Animal2 animal2 = new Animal2();
public void run() {
// 组合
animal2.run();
System.out.println("飞!");
}
public void eggSheng() {
System.out.println("卵生");
}
}
is-a 关系使用继承。
是关系。
has-a关系使用组合。
拥有关系。
1.20 final关键字
修饰变量:
- 常量
修饰方法:
- 该方法不能被子类重写,但是可以被重载
修饰类:
- 修饰的类不能有子类,不能被继承。例如Math、String
// Animal 类
public /*final*/ class Animal { // 修饰的类不能被继承
public /*final*/ void run() {
// final加到方法前面,则该方法不能被子类重写。
System.out.println("跑");
}
public static void main(String[] args) {
final int MAX_VALUE = 200; // 常量
// 不能再修改
// MAX_VALUE = 100;
}
}
class Bird extends Animal {
public void run() {
super.run();
System.out.println("飞起来");
}
}
1.21 封装/隐藏
encapsulation
capsul 胶囊,太空舱
为什么需要封装:
隐藏对象内部的复杂性,只对外公开简单的接口。便于外界调用,从而提高系统的可扩展性、可维护性。
高内聚,低耦合。
- 高内聚:类的内部数据操作细节自己完成,不允许外部干涉。
- 低耦合:仅保罗少量的方法给外部使用。
使用访问控制符,实现封装:
访问控制符 | 同一个类 | 同一个包中 | 子类(即使是不同包) | 所有类 |
---|---|---|---|---|
private | * | |||
default | * | * | ||
protected | * | * | * | |
public | * | * | * | * |
类的属性处理:
- 一般使用private,除非本属性确定会让子类继承
- 提供相应的get/set方法来访问相关属性,这些方法通常是public,从而提供对属性的读取操作。(boolean变量的get方法是用:is开头)
- 一些只用于本类的辅助性方法可以用private。
1.22 多态
1.22.1 定义
polymorphism
编译 -> 运行
说 -> 做
主要是用来实现动态联编的。程序的最终状态只有在执行过程中才能被决定而非在编译阶段就决定了。这对于大型系统来说能提高系统的灵活性和扩展性。
JAVA中如何实现多态?
引用变量的两种类型:
- 编译时类型(模糊一点,一般是一个父类)
- 由声明时的类型决定
- 运行时类型(运行时,具体是哪个子类就是哪个子类)
- 由实际对应的对象类型决定
多态的存在要由3个必要条件:
要有继承,要有方法重写,父类引用指向子类对象
Animal类
public class Animal {
public void voice() {
System.out.println("普通动物叫声");
}
}
// 继承,重写方法
class Cat extends Animal {
@Override
public void voice() {
System.out.println("喵喵");
}
public void catchMouse() {
System.out.println("抓老鼠");
}
}
class Dog extends Animal {
@Override
public void voice() {
System.out.println("汪汪");
}
public void watchDoor() {
System.out.println("看门");
}
}
class Pig extends Animal {
@Override
public void voice() {
System.out.println("哼哼");
}
}
// Test类
public class Test {
// 没有多态的情况
/*
public static void testAnimalVoice(Cat c) {
c.voice();
}
public static void testAnimalVoice(Dog c) {
c.voice();
}
public static void testAnimalVoice(Pig c) {
c.voice();
}
*/
// 使用多态
public static void testAnimalVoice(Animal c) {
c.voice();
// C是Cat的实例对象
if (c instanceof Cat) {
((Cat) c).catchMouse();
}
}
public static void main(String[] args) {
Cat c = new Cat();
// 类似这样的代码
Animal a = c;
// 父类引用指向子类对象
Animal c1 = new Cat();
Animal d = new Dog();
Animal p = new Pig();
testAnimalVoice(c1);
testAnimalVoice(d);
testAnimalVoice(p);
// 错误,Animal没有该方法,编译器不认
// c1.catchMouse();
// 强制转型
Cat a2 = (Cat)c1;
a2.catchMouse();
// 编译器不报错,但运行过程报错
// Cat a3 = (Cat)d;
// a3.catchMouse();
}
1.22.2 多态内存分析
Test类
public class Test {
// ① 开辟c新变量
// ② c指向对象
public static void testAnimalVoice(Animal c) {
// 执行对象的方法
c.voice();
if (c instanceof Cat) {
((Cat) c).catchMouse();
}
}
public static void main(String[] args) {
// ① 从Java Test开始,加载Test类
// ② 加载Animal类。
// ③ a这个变量
// ④ 加载Cat类,以及Animal类,Object类,具体见内存图
Animal a = new Cat();
// ① 开辟a2新变量
// ② a2指向对象
Cat a2 = (Cat) a;
testAnimalVoice(a2);
}
}
1.22.3 多态内存分析深化
建立三个类:
HttpServlet类
public class HttpServlet {
public void service() {
System.out.println("HttpServlet.service()");
// 调用子类的DoGet()
// 调用最外面的doGet();
doGet();
// 类似下面这种
this.doGet();
}
public void doGet() {
System.out.println("HttpServlet.doGet()");
}
}
MyServlet类
package cn.oop.polymorphism.myservlet;
public class MyServlet extends HttpServlet {
@Override
public void doGet() {
System.out.println("MyServlet.doGet()");
}
}
package cn.oop.polymorphism.myservlet;
public class Test {
public static void main(String[] args) {
// 加载HttpServlet类,创建对象
// 创建Myservlet对象
HttpServlet s = new MyServlet();
// 调用了HttpService的service。
s.service();
}
}
Test类
# 结果如下:
HttpServlet.service()
MyServlet.doGet()
MyServlet.doGet()
1.23 抽象类
abstract
简单来说就是只有方法的声明,没有方法体。但是子类必须要重写抽象方法。
- 是一种模板模式。抽象类为所有子类提供了一个通用模板,子类可以在这个模板基础上进行扩展。
- 通过抽象类,可以避免子类设计的随意性。通过抽象类,我们就可以做到严格限制子类的设计,使得子类之间更加通用。
要点:
- 有抽象方法的类只能定义成抽象类
- 抽象类不能实例化,即不能用new来实例化抽象类。
// 不能这样。
Animal a = new Animal();
// 可以这样。
Animal a = new Animal() {
// 重载抽象方法等等
};
- 但是抽象类可以包含属性、方法、构造方法。但是构造方法不能用来new实例,只能用来被子类调用。
// 这样就可以了
Animal a = new Cat();
- 抽象只能用来继承
- 抽象方法必须被子类实现
抽象方法的意义在与设计和实现分开。
1.24 接口
interface
抽象类更深一步
public interface MyInterface {
// 接口中只有:常量和抽象方法!
// 接口中常量是这三个,写不写都有
/* public static final */ String MAX_GREAD = "Boss";
int MAX_SPEED = 120;
// 默认是public abstract,写不写都是
/* public abstract*/ void test01();
public int test02(int a, int b);
}
类似公司的规章制度。
对于设计人员:设计接口,
对于编码人员:实现。
public class MyClass implements MyInterface {
@Override
public void test01() {
String name = MyInterface.MAX_GREAD;
System.out.println("test01");
}
@Override
public int test02(int a, int b) {
System.out.println(a + b);
return 0;
}
}
描述更抽象的关系,继承不好继承,所有用接口更好一些。
例如飞机、战斗机、子弹,石头等等。
继承不方便。但是可以利用飞的共性。
public interface Flyable {
int MAX_SPEED = 11000; // 飞行速度
int MIN_HEIGHT = 1; // 飞行高度
void fly();
}
class Plane implement Flyable {
@Override
public void fly() {
System.out.println("依靠发动机在飞");
}
}
- 接口就是比抽象类还抽象的抽象类。规范和具体实现的分离。
- 接口就是规范,定义的是一组规则,体现了现实世界中“如果你是,则必须能。。”的思想
- 接口的本质是契约。制定好之后必须遵守。
- 接口命名之前加个I更好一些
[访问修饰符] interface 接口名 [extends 父接口1,父接口2… ] {
-常量定义 //总是:public static final
-方法定义 //总是:public abstract
}
- 子类通过implements 来实现接口中的规范
- 接口不能创建实例,但是可用于声明引用变量类型
- 一个类实现了接口,必须实现接口中所有的方法,并且这些方法只能是public的。
- 接口支持多继承。
public interface InterfaceA {
void aaa();
}
interface InterfaceB {
void bbb();
}
interface InterfaceC extends InterfaceA, InterfaceB {
void ccc();
}
class TestClass implements InterfaceC {
@Override
public void aaa() {
// TODO Auto-generated method stub
}
@Override
public void bbb() {
// TODO Auto-generated method stub
}
@Override
public void ccc() {
// TODO Auto-generated method stub
}
}
1. 25 回调实现
CallBack
Hook 钩子函数
模板方法模式
说明白就是多态
类似:
写代码其中有一行不知道怎么实现。在这行挂个钩。等之后再来实现。
像个钩子里面。多态里面的应用。
PointFrame类
public class PaintFrame {
public static void drawFrame(MyFrame f) {
System.out.println("启动线程");
System.out.println("增加循环");
System.out.println("查看消息栈");
// 画窗口 这里类似一个钩挂在这里
// 之后定义一个类,直接挂在这里
f.paint();
System.out.println("启动缓存,增加效率");
}
public static void main(String[] args) {
drawFrame(new GameFrame01());
drawFrame(new GameFrame02());
}
}
class GameFrame01 extends MyFrame {
@Override
public void paint() {
System.out.println("GrameFram01.paint()");
System.out.println("画窗口");
}
}
class GameFrame02 extends MyFrame {
@Override
public void paint() {
System.out.println("GrameFram02.paint()");
System.out.println("画窗口");
}
}
MyFrame
public class MyFrame {
public void paint() {
System.out.println("把自己窗口画出来");
}
}
1.26 内部类
innerclasses
内部类的作用:
- 内部类提供了更好的封装。只能让外部类直接访问,不允许同一个包中的其他类直接访问。
- 内部类可以访问外部类的私有属性,内部类被当成其外部类的成员。但外部类布恩那个访问内部类的内部属性。
内部类的使用场合
由于外部类提供了更好的封装特性,并且可以很方便的访问外部类的属性。所有,通常内部类在只为所在外部类提供服务的情况下优先使用。
内部类的分类:
成员内部类(内部类仿佛是我的外部类成员一样):
可以使用private、protected、public、任意进行修饰
非静态内部类
类似普通成员。- 必须寄存在一个外部类对象里面。因此,如果有一个非静态内部类对象,那么一定存在对应的外部类对象。非静态内部类对象单独属于外部类的某个对象。
- 非静态内部类可以使用外部类的成员,但是外部类不能直接访问非静态内部类成员。
- 不能有静态方法、静态属性、静态初始化块。
- 内部变量访问要点:
- 内部类里方法的局部变量,变量名
- 内部类属性,this变量名
- 外部类属性,外部类名this变量名
- 静态内部类
类似静态属性。
- 定义方法:
static class ClassName{
//类体
} - 使用要点:
- 当一个静态内部类对象存在,并不一定存在对应的外部类对象。因此,静态内部类的实例方法不能直接访问外部类的实例方法。
- 静态内部类看作外部类的一个静态成员。因此,外部类的方法中可以通过,静态内部类.名字 访问静态内部类的静态成员。通过new 静态内部类()访问内部类的实例。
- 在外部类的外面创建静态内部类
- 定义方法:
Face.TestStaticInner aInner = new Face.TestStaticInner();
- 局部内部类
定义在方法内部,作用域只限于本方法。用的非常少。
import com.bjsxt.eleven.Face.Nose;
public class Outer {
public static void main(String[] args) {
// 直接调是错误的
// Nose n = new Nose();
Face f = new Face();
Nose n = f.new Nose();
Face.Nose n1 = f.new Nose();
n.breath();
}
}
class Face {
int type;
String shape = "瓜子脸";
static String color = "红润";
// 内部类
class Nose {
String type1;
void breath() {
// 调用外部类
System.out.println(Face.this.type);
System.out.println(shape);
System.out.println("呼吸!");
}
// 这个类对象从属于外部类对象,所以不能有静态属性和方法
/*
static void eat(){
}
*/
}
static class Ear {
void listen() {
// 静态内部类无法使用外部类成员
// System.out.println(shape);
System.out.println(color);
System.out.println("我在听");
}
}
}
总结一下:
- 普通的成员内部类可以访问外部类的普通的属性和方法。
- 普通的成员内部类可以看作外部类的普通的属性和方法。
- 调用内部类,先有外部类再有内部类
- 静态内部类,它存在,外部类不一定存在,所以不能访问外部类。
- 静态内部类可以直接访问外部类的静态的属性和方法。
匿名内部类:
适合那些只需要使用一种的类。
new 父类构造器(实参列表) 实现接口() {
// 匿名内部类类体
}
this.addWindowListener(new WindowAdapter(){
public void windowClosing(WindowEvent e) {
System.exit();
}
});