一、内部类
当一个事物的内部,还有一个部分需要一个完整的结构进行描述,而这个内部的完整的结构又只为外部事物提供服务,那么整个内部的完整结构最好使用内部类。在Java中,可以将一个类定义在另一个类或者一个方法的内部,前者称为内部类,后者称为外部类,内部类也是封装的一种体现。
public class OutClass {
class InnerClass{
}
}
//OutClass是外部类
//InnerClass是内部类
注意事项:
1.
而且会发现:java中会把它们存放在两个文件中,而且一个文件中存放两个类的话最多只能有一个被public修饰的类。
2.内部类和外部类共用同一个java源文件,但是经过编译之后,内部类会形成单独的字节码文件。 解释如下:
public class OutClass {
class InnerClass{
}
public static void main(String[] args) {
System.out.println("test");
}
}
会发现没有单独生成一个InnerClass的.java文件。
根据内部类定义的位置不同,一般可以分为以下几种形式:
1.成员内部类(普通内部类:被static修饰的成员内部类 和 静态内部类:未被static修饰的成员内部类)
2.局部内部类(不谈修饰符)、匿名内部类(先不看)
注意:内部类其实日常开发中使用不是非常多,大家在看一些库中的代码时候可能遇到的比较多,日常使用最多的还是匿名内部类。
//外部类
public class OutterClass {
int a=10;
//成员内部类
//内部类:普通内部类
class InnerClass1{
}
//内部类:静态内部类
static class InnerClass2{
}
///
//在一个方法内部定义内部类
//实例代码块
{
System.out.println("hello");
//内部类:局部内部类
class InnerClass3{
}
}
//静态代码块
static {
System.out.println("hello");
//内部类:局部内部类
class InnerClass4{
}
}
public void method(){
//内部类:局部内部类
class InnerClass5{
}
//普通代码块
{
//内部类:局部内部类
class InnerClass6{
}
}
}
public static void main(String[] args) {
}
}
1.成员内部类
在外部类中,内部类定义位置与外部类成员所处的位置相同,因此成为成员内部类。
1.普通内部类
即未被static修饰的成员内部类。
public class TestInnerClass {
int a;
public void methodA(){
}
//成员内部类:普通内部类
public class InnerClass{
int b;
void methodB(){
}
}
public static void main(String[] args) {
//普通类:成员使用--->先构造对象,然后通过对象访问其内部的成员
TestInnerClass ti=new TestInnerClass();
ti.methodA();
//如何使用成员内部类-----普通内部类
//只能借助外部类的对象来创建内部类的对象
InnerClass ic=ti.new InnerClass();
ic.methodB();
}
}
成员内部类和方法所处的位置是一样的,所以方法怎么用,内部类就应该怎么用,内部类对象就应该怎么创建。图下这种方式是不对的,会飘红。
那么在其他文件中内部类应该怎样使用呢?
上图这种方式会飘红,InnerClass编译器不能识别InnerClass—因为该类在TestInnerClass内部定义。 那么为什么在原方法中就可以呢?原因是:上面实例化是在main方法中,而main方法是在TestInnerClass中,自然是不用重新写。正确写法:
//在不同的文件中创建内部类的对象
//测试TestInnerClass中内部类的使用
void func(){
TestInnerClass ti=new TestInnerClass();
//要实例化TestInnerClass中内部类的对象
TestInnerClass.InnerClass tii=ti.new InnerClass();
}
注意:这里直接敲InnerClass按enter键就会出来前边的。
那么内部类能不能直接使用外部类中的成员或者方法呢?
public class TestInnerClass {
int a;
int b;
public void methodA(){
}
//成员内部类:普通内部类
public class InnerClass{
int b;
//实际上编译器会给内部类再增加一个引用类型的变量 TestInnerClass this$0; //将来指向外部类的对象
void methodB(){
a=100; // ===>TestInnerClass.this.a=100;
methodA(); // ===>TestInnerClass.this.methodA();
b=200; //给内部类自己的成员变量b赋值 this.b=200
TestInnerClass.this.b=300;
}
}
public static void main(String[] args) {
//普通类:成员使用--->先构造对象,然后通过对象访问其内部的成员
TestInnerClass ti=new TestInnerClass();
ti.methodA();
//如何使用成员内部类-----普通内部类
//只能借助外部类的对象来创建内部类的对象
InnerClass ic=ti.new InnerClass();
ic.methodB();
}
}
编译器会给内部类维护一个外部类对象的引用:this$0
注意事项:
1.外部类中的任何成员都可以被在普通内部类方法中直接访问。
2.普通内部类所处的成员与外部类成员位置相同,因此也受public、private等访问限定符的约束。
3.在内部类方法中访问同名的成员时,优先访问自己的,如果要访问外部类同名的成员,必须:外部类名称.this.同名成员 来访问。
4.普通内部类对象必须在先有外部类对象前提下才能创建。
5.外部类中,不能直接访问内部类中的成员,如果要访问必须先要创建外部类的对象。
正确示例:
下面对上面讲的程序进行详细的图示解释:
普通内部类:
1.在内部类中可以访问外部类任何成员,不论静态成员还是普通成员。
2.在外部类中不能直接访问内部类的成员,如果要访问,只能在外部类方法中,先实例化内部类的对象,然后通过该对象访问内部类的成员。
同时要强调:
2.静态内部类
1.在本文件中进行创建:
public class TestInnerClassStatic {
int a;
int b;
static int c;
void methodA(){
System.out.println("methodA()");
}
//静态内部类---被static修饰的成员内部类
static class InnerClass{
int d;
static int e;
void methodC(){
}
}
public static void main(String[] args) {
//静态成员变量访问
System.out.println(TestInnerClassStatic.c);
//静态内部类对象的创建
//不需要借助外部类对象来创建了
InnerClass ic=new InnerClass();
}
}
2.在其他文件中:
//测试在其他文件中创建静态内部类的对象
public class StaticInnerClass {
public static void main(String[] args) {
//静态内部类对象可以直接创建,不需要依赖外部类对象
TestInnerClassStatic.InnerClass tic=new TestInnerClassStatic.InnerClass();
}
}
那么在静态内部类中能不能直接访问外部类中的成员变量呢?
答案是:只能访问外部类中的静态成员。
注意事项:
1.在内部类中只能访问外部类中的静态成员和静态成员函数。
2.创建内部类对象时,不需要先创建外部类对象。
3.成员内部类,经过编译之后会生成独立的字节码文件,命名格式为:外部类名称$内部类名称。
2.局部内部类
定义在内部类的方法体或者{}中,该种内部类只能在其定义的位置使用,一般使用的非常少,此处简单了解下语法格式。
//测试局部内部类
public class ScopedInnerClass {
public void method(){
int a=10;
System.out.println(a);
//局部内部类:该内部类只能在该方法中使用,其他位置都使用不了
//不能被访问修饰符限制---public、private
//不能在局部内部类中定义静态的成员
/*public*/class InnerClass{
int b;
//static int c;
void func(){
}
/*
static void func2(){
}
*/
}
InnerClass ic=new InnerClass();
}
public static void main(String[] args) {
//编译失败,局部内部类只能在其定义的方法内部使用
InnerClass ic=null;
}
}
但是虽然说之前提到的普通内部类和静态内部类可以在任意位置进行使用,但是我们也可以限制让他们只能在当前类的内部进行使用。可以加访问修饰符限制。
注意事项:
1.局部内部类只能在所定义的方法体内部使用。
2.不能被public、static等修饰符修饰。
3.编译器也有独立的字节码文件,命名格式:外部类名字$x内部类名字.class,x是一个整数。
4.几乎不会使用。
二、对象的打印
首先我们创建一个Person类,一般情况下,成员变量都设置为private,但是设置为private之后,别人就没有办法进行使用,那么就可以给这些私有的成员提供set/getXXX的访问方式。示例如下:
public class Person4 {
private String name;
private String gender;
private int age;
public void setAge(int age){
this.age=age;
}
public int getAge(){
return age;
}
}
如果类中私有的成员比较多,自己来写太麻烦了,可以利用generate:
设置构造器也可以用generate(之前谈到过)。
主函数(之前我们都只打印变量的值,这次试着打印对象的值,看会输出什么结果):
public static void main(String[] args) {
int a=10;
System.out.println(a);
Person4 p=new Person4("HanMeiMei","女",12);
System.out.println(p);
}
运行结果:
会发现输出的是地址。那么假如说我不想打印地址,想要打印对象的值应该怎么样操作呢?
重写toString:
就会添加这样一段程序:
@Override
public String toString() {
return "Person4{" +
"name='" + name + '\'' +
", gender='" + gender + '\'' +
", age=" + age +
'}';
}
输出结果为:
那么假如我想让他换一种方式显示可以么?是可以的。哈哈哈,自问自答。
@Override
public String toString() {
String s="[";
s+=name;
s+=",";
s+=gender;
s+=",";
s+=age;
s+="]";
return s;
}
输出结果: