第六章类和对象

目录

初步认识类和对象

面向过程和面对对象的区别 

类和类的实例化 

类的成员 

字段

方法(method) 

 static关键字

修饰属性

static修饰方法 

static修饰类 

封装思想

Private实现封装 

Default

构造方法 

JKD提供的默认构造方法 ​编辑

构造方法的重载

this的使用

​编辑

调用当前对象的成员属性 

this调用当前对象的方法

this调用类的成员方法

 this表示构造函数之间的相互调用

 this表示当前对象的引用

代码块 

普通代码块 

成员代码块

静态代码块

匿名对象 

toString方法 

内部类 

四大内部类

成员内部类

静态内部类

方法内部类

匿名内部类


初步认识类和对象

面向过程和面对对象的区别 

  • 在C语言中,是面对过程的编程语言
  • JAVA和C++是面对对象的编程语言,在JAVA中,一切皆对象 

什么是面对过程呢,比如拿狗吃狗粮这件事来举例字,在C中关注的是狗吃狗粮这件事情,所有的行为和属性也罢,在C中都是一个接着一个的方法的调用,关注的是行为的本身

而对于JAVA来说 ,狗吃狗粮可以看成 狗 + +狗粮,将世界上所有的行为都归结为 对象+行为+对象,行为一定是从某个对象出发,然后作用于另一个对象或某些对象,所以JAVA是注重对象,JAVA中编程一切围绕着对象进行,找对象,建对象,用对象,并维护对象之间的关系

对于一些规模较小的问题 将其分解为过程的开发方式比较理想。而面向对象更加适用
于解决规模较大的问题 耍想实现一个简单的 Web 浏览器可能需要大约 2000 个过程,这些 过程可能需要对一组全局数据进行操作。采用面向对象的设计风格, 可能只需要大约 100 个 类, 每个类平均包含 20 个方法(上图所示  )。后者更易于程序员掌握, 也容易找到bug
假设给定对象的数据出错了 在访问过这个数据项的 20 个方法中查找错误要比在 2000 个过 程中查找容易得多

类和类的实例化 

类就是一类对象的统称,对象是这一类的实例化
public class test {
    public static void main(String[] args) {
        Person lsc=new Person();//对象的引用和类的实例化
        Person lkx=new Person();
        lsc.age=22;//调用类的成员变量
        lsc.name="刘颂成";
        lsc.sex="男";
        lsc.print();
    }
}
class Person{//Person这个类的创建
    int age;//成员变量
    String name;
    String sex;
    void eat(String food){//成员方法
        System.out.println(name+"吃"+food);
    }
    void print(){
        System.out.println("姓名为"+name+" 年龄为"+age+" 性别为"+sex);
    }
}

 如何理解类和类的实例化和对象的引用呢?

拿我和彭于晏举例子,首先我们两都是实实在在存在的,都能被当成一个对象,那我和彭于晏有啥相同和区别呢?首先我们都有五官,他有的东西,我也有,其他任何人类也是都有的,他能呼吸,我也能呼吸,其他人类也能呼吸,但是区别是在我们都有的属性但是我们的属性值不同,我们虽然都有五官,但是我的五官比彭于晏帅,但是我们呼吸这种行为的属性值是不同的,比如呼吸的频率不同

  • 因此我们把具有相同属性和行为的一类对象抽象为类,比如我们都是人类这个类,使用类来描述这类对象的特性
  • 类的抽象就是,人类,猫类,犬类,无法具体到某个实体,所以用实例化将类实例化对象,比如把人类实例化为我这个人,但是对象是某个类的实体,这些属性都会具有属性值,行为也会有相对的的意义
  • 啥是对象的引用呢,比如我是一个实实在在的人,是一个对象,我叫刘颂成,也叫颂成,我的身份证号码,这些名字和字符串都是对我这个对象的引用,通过给我取名字来调用我这个对象(别人可以叫我刘颂成来让我干一些事情,比如我妈叫我刘颂成来吃饭)

注意点

  • 类的命名使用大驼峰的命名法
  • 类中定义的成员变量都有默认值,方法的局部变量没有默认值
  • 用new来来创建一个对象的实例
  • 用.操作符来访问对象的属性和方法
  • 同一个类可以创建多个实例
  • 一个源文件只能有一个public class主类

类的成员 

类的成员包括字段,方法,代码块,内部类,接口等

字段

字段也称为属性或者成员变量

class Person {
    public String name;   // 字段
    public int age; }
class Test {
    public static void main(String[] args) {  
        Person person = new Person();
        System.out.println(person.name);
        System.out.println(person.age);
   }
}

注意点

  • 使用.来访问成员的字段
  • 访问包含读也包含写
  • 对于一个成员的字段,如果没有被赋予初值,就是设置成该类型的默认值(整数是0,实数是0.0,布尔类型是false,引用类型是null)

null的错误引用

 如果对null进行引用,则会发生错误

————————————————————————————————————————

 字段就地初始化

class Person {
    public String name = "张三";
    public int age = 18;
 }
class Test {
    public static void main(String[] args) {
        Person person = new Person();
        System.out.println(person.name);
        System.out.println(person.age);
   }
}
很多时候我们不希望字段使用默认值 , 而是需要我们显式设定初值 . 可以这样写
 
 

方法(method) 

用一个方法来描述对象的一个行为

public class test {
    public static void main(String[] args) {
        Person lsc = new Person();//对象的引用和类的实例化
        lsc.age = 22;//调用类的成员变量
        lsc.name = "刘颂成";
        lsc.sex = "男";
        lsc.print();
    }
}
class Person {//Person这个类的创建
        int age;//成员变量
        String name;
        String sex;
        void eat(String food) {//成员方法
            System.out.println(name + "吃" + food);
        }
        void print() {
            System.out.println("姓名为" + name + " 年龄为" + age + " 性别为" + sex);
        }
    }

用print这个方法,来实现lsc这个对象的自我介绍

 static关键字

static可以修饰属性(类属性),方法(工具方法),代码段(静态代码块),和修饰内部类(静态内部类)

修饰属性

static修饰的属性叫做类属性,类变量,是可以让所有的对象共享

例子

public class test {
    public static void main(String[] args) {
        Person lsc=new Person();
        Person lkx=new Person();
        lsc.sex="男";
        lsc.name="刘颂成";
        lsc.age=22;
        lkx.age=18;
        lkx.sex="女";
        lkx.name="李可新";
        Person.country="中国";
        lsc.print();
        lkx.print();
    }
}
class Person{
    String name;//成员变量,在堆区存储
    int age;
    String sex;
    static String country;//类变量在方法区存储
    void print(){
        System.out.println("姓名为"+name+" 年龄为"+age+" 性别为"+sex+" 国籍为"+country);
    }
}

 ————————————————————————————————————————

为什么引入static来修饰属性?

在上面的例子我们可以看到country这个变量,很多人的属性值是一样的,那如果我们光复了日本,那我是不是也要把所有日本人的国籍改成中国,那样需要一个一个改,太麻烦,所以对很多对象共有相同属性值的属性,我们可以用static来修饰,表示这个属性是很多对象的共同拥有的,只要修改一次就可以将所有对象相应的类属性都改变

————————————————————————————————————————

static修饰属性时的性质

  • static修饰的属性,在JVM的方法区存储,所有该类的所有的对象共享此属性
  • static修饰的属性,表示是这个类的属性,所以无需使用对象访问,直接用类名称调用即可,但是也可以用对象来调用,但是最好用类名称调用,因为不规范
  • 修改一些类属性的值,属于该类所有的对象的该属性值都会变
public class test {
        public static void main(String[] args) {
        Person lsc=new Person();
        Person lkx=new Person();
        lsc.sex="男";
        lsc.name="刘颂成";
        lsc.age=22;
        lsc.country="世界";//用对象调用类属性
        lkx.age=18;
        lkx.sex="女";
        lkx.name="李可新";
        lkx.country="宇宙";//用对象调用类属性
        Person.country="中国";
        //用类名调用类属性,最后一次修改,所以所有对象的country属性是中国
        lsc.print();
        lkx.print();
    }
}
class Person{
    String name;//成员变量,在堆区存储
    int age;
    String sex;
    static String country;//类变量在方法区存储
    void print(){
        System.out.println("姓名为"+name+" 年龄为"+age+" 性别为"+sex+" 国籍为"+country);
    }
}

————————————————————————————————————————

图解static和普通成员变量在内存中情况

1 初始化

2 在mian函数中赋值了 

方法区的存储什么数据

  •  类中的所有方法都在这个方法区存储
  • 常量和静态变量 

————————————————————————————————————————

在JAVA中是否能在一个方法内定义一个static变量?

答案是否定的 

为什么呢,因为定义在方法中的变量都是局部变量,都是存在栈区的,但是static是静态变量,是存在方法区的,怎么可能存在一个即在栈区,又在方法区的变量呢

————————————————————————————————————————

在类中final和static的区别和联系

  • final是修饰成员常量,在堆区存储
  • static修饰静态变量,在方法区存储
  • 通常我们在类中用final定义一个成员常量都是需要将static final共同使用,成为类的常量

static修饰方法 

static修饰方法叫做类方法,工具方法,静态方法,也是可以通过类型直接调用,没有对象也能访问

为什么主方法是一个静态方法?

 因为主方法是一个程序的入口,如果主方法是一个成员方法,那必须通过对象调用,但是入口都没有,从哪产生对象呢,程序从主方法开始执行,主方法要能调用起来,静态方法,直接调用,无需产生对象

————————————————————————————————————————

静态方法是否能访问成员方法和成员变量?

答案是不行的,因为静态方法的调用不需要对象,哪怎么可能可以调用成员变量和成员方法

成员方法是否能访问静态变量和静态方法?

调用成员方法,肯定已经有对象了,没有对象都能调用静态方法,现在有对象,肯定能调用

注意点

  • 在静态方法中只能调用静态方法或者静态属性,static家族可以相互调用,不能直接调用成员方法或者成员属性,必须通过对象调用
  • 工具类的方法设计为static方法
  • 看见static,斗都与对象无关,直接通过类名称和静态属性和方法

————————————————————————————————————————

一个null小细节


class Test {
	public static void hello() {
	    System.out.println("hello");
	}
}
public class MyApplication {
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Test test=null;
		test.hello();
	}
}

这个代码运行起来,并不会发生错误,为什么呢,因为hello这个方法是静态的,所以是属于Test这个类的,test.hello,发挥作用的还是Test.hello(),所以并不会发生空指针异常

static修饰类 

static只能修饰内部类

普通的类为什么不能用static修饰?

 类定义出来就是用来产生对象的,假设这个类用static来修饰类,这个类就没有对象就能调用

封装思想

面对对象一共有三种特性,封装,多态和继承,而封装是为了实现程序的保护性和易用性,让使用者不必太知道实现者是如何实现的,只要能用就行,降低了使用者的学习量

例子

拿银行卡这个类来说,银行卡有卡号,余额,密码三个属性,如果这三个属性都直接暴露在外部肯定是不合理的(直接就在卡上贴着)——>不安全,不能让这些属性可以通过对象直接调用

对于汽车这个类来说,车真正发动起来,我们需要启动发动机,变速箱,等等属性,但是现实中,我们只需要一键启动就行,对于这些具体的属性是不可见的,也是我们不关注的——>易用性

权限修饰符

指的是修饰的属性,方法,类,可见的范围有多大,一共有四大访问修饰符,可见范围从小到达

private<default<protected<public

  • private 表示私有的,被private修饰的属性和方法,只在当前类内部可见,出来类的范围,对外部就完全隐蔽了,外部不知道其的存在
  • default 不写任何修饰权限的修饰符,表示的就是default,包访问权限
  • protected
  • public 公开的,被public修饰的,当前整个程序(项目)都是可以使用的

Private实现封装 

 只有Bank内部知道这三个属性的存在,在这个类的外部,是不知道这三个属性的存在,不能直接调用

那如何在外部去实现这些私有属性呢?

需要使用这个类提供的getter(取值)和setter(修改值)的方法,而且哪些属性要提供setter,哪些属性只提供getter,或者都提供,需要由这些属性来决定

import java.util.Scanner;

public class PrivateTest {
    public static void main(String[] args) {
        Bank bank=new Bank();
        System.out.println(bank.getCardNum());
        System.out.println(bank.getSal());
        bank.setPassword();
    }
}
class Bank{
    private int cardNum=145678;
    private double sal=20000.6;
    private String password="12345678";

    public int getCardNum() {
        return cardNum;
    }

    public double getSal() {
        return sal;
    }

    public void setPassword() {
      int count=0;
      Scanner scanner=new Scanner(System.in);
      while (true){
          System.out.println("请输入原来的密码");
          String oldPassword=scanner.nextLine();
          count++;
          if (oldPassword.equals(password)){
              System.out.println("验证成功,请输入你的新密码");
              String newPassword=scanner.nextLine();
              password=newPassword;
              System.out.println("密码修改成功");
              break;
          }
          else {
              System.out.println("验证失败,请查证后重新输入");
              if (count==3){
                  System.out.println("尝试次数过多,银行卡已经锁定");
                  break;
              }
          }
      }
    }
}

实现了对银行卡这个类的属性包含,让别人不能直接修改卡号和余额,只能通过Bank这个类的类方法去提取卡号和余额的数据,能改动密码,但是必须按照Bank这个类的规定的方法去改,实现了安全性

public class PrivateTest {
    public static void main(String[] args) {
        Car car=new Car();
        car.start();
    }
}
class Car{
    private String engine;
    private String bianSuXiang;
    private String guLu;
    public void start(){
        engine="引擎启动";
        bianSuXiang="启动变速箱";
        guLu="轮胎开始转";
        System.out.println("汽车启动了");
    }
}

对于Car这个类,我们假设启动汽车,不需要知道其内部如何构成,只需要调用启动这个函数,就直接可以启动

阿里编码规范 JAVA类中所有的成员变量一律使用private封装,并根据自身属性实际情况对我提供getter和setter方法

Default

default就是什么都不写的限权标识符,默认的就是包访问限权,表示的范围就是在一个包中,子包和包外都不行

  • 同一个包下的类可以调用default修饰权限的属性或者方法
  • 不同包下的类不能使用default修饰的属性或者非方法
  • 相同包下不同的子包也不能使用default修饰的属性和方法

包(Package)的注意点

  • 在不同的包中可以定义相同名称的类
  • 包的命名使用全小写,多个单词下划线分割
  • 引用一个包的内容,需要import 包名.类名(类的全名就是包名.类名)不能直接导入一个包,只能导入一个包下面的某个类

构造方法 

构造方法是类中非常特殊的一类方法,使用new实例化对象时,其实上就是调用的是该类的构造方法,构造方法的作用就是产生对象,为类的成员变量赋值

使用关键字new产生一个对象时,大致分为两步

  • 为对象在堆中分配空间
  • 调用对象的构造方法为对象成员变量赋值

构造方法的语法规则

  • 方法名称必须要与类名称完全相同
  • 构造方法没有返回值声明,不是void
  • 一个类中至少存在一个构造方法,若没有显示定义,编译器会生成一个默认的无参构造
  • 对象不能自己调用构造方法(JVM产生对象的时候调用构造方法,对象实例化结束,无法在程序中手动调用构造方法再次实例化对象,就像你妈生你,然后你自己再生自己一遍)

JKD提供的默认构造方法 

图二是Dog这类编译后的代码,我们发现在Dog类中多出了Dog这个无参构造方法

我们自己构造构造方法时的细节

 编译后的Dog代码

注意点

  • 如果我们自己定义构造函数时,JDK就不会再为我们生成默认的无参构造函数
  • 每当我们构造一个Dog类的对象,就会执行一次构造函数

 内存分析产生新对象时的步骤

简单类对象的实例化过程

  1、在方法区加载类;

  2、在栈内存申请空间,声明变量P;

  3、在堆内存中开辟空间,分配对象地址;

  4、在对象空间中,对对象的属性进行默认初始化,类成员变量显示初始化;

  5、构造方法进栈,进行初始化;

  6、初始化完成后,将堆内存中的地址赋给引用变量,构造方法出栈;

子类对象的实例化过程

  1、在方法区先加载父类,再加载子类;

  2、在栈中申请空间,声明变量P;

  3、在堆内存中开辟空间,分配对象地址;

  4、在对象空间中,对对象的属性(包括父类的属性)进行默认初始化;

  5、子类构造方法进栈;

  6、显示初始化父类的属性;

  7、父类构造方法进栈,执行完毕出栈;

  8、显示初始化子类的属性;

  9、初始化完毕后,将堆内存中的地址值赋给引用变量P,子类构造方法出栈;

 

 当成员变量定义时赋值的具体运行

public class Test {
    public static void main(String[] args) {
        Dog dog=new Dog("哈士奇");
        Dog dog1=new Dog();
        System.out.println(dog.name+" "+dog.age);
        System.out.println(dog1.name+" "+dog1.age);

    }
}
class Dog{
     String name="金毛";
     int age=10;
    public Dog(String n){
        name=n;
        System.out.println("Dog的有参构造方法");
    }
    public Dog(){
        System.out.println("Dog的无参构造方法");
    }
}

                                   我们画一下其编译的后的构造方法的代码

  在编译的时候,这段代码会按照这个样子执行,先初始化再赋值,成员变量的赋值都是在构造方法里面执行的

构造方法的重载

构造方法就是为了类中的成员变量赋值,此时的重载只可能是参数的个数不同(因为成员变量的类型在类定义时就指定好了,只是在new对象时初始化变量的个数不同)

public class ClassOverload {
    public static void main(String[] args) {
        Cat cat1=new Cat();
        Cat cat2=new Cat("布偶");
        Cat cat3=new Cat("蓝猫",2);
        Cat cat4=new Cat("中华田园猫",3,"黄色");
        cat1.print();
        cat2.print();
        cat3.print();
        cat4.print();
    }
}
class Cat{
    private String name;
    private int age;
    private String color;
    public Cat(){
        System.out.println("这是Cat的无参构造方法");
    }
    public Cat(String n){
        name=n;
        System.out.println("这是Cat的一个name参数构造方法");
    }
    public Cat(String n,int a){
        name=n;
        age=a;
        System.out.println("这是Cat的name+age参数构造方法");
    }public Cat(String n,int a,String c){
        name=n;
        age=a;
        color=c;
        System.out.println("这是Cat的name+age+color参数构造方法");
    }
    public void print(){
        System.out.println("种类为"+name+" 年龄为"+age+" 颜色为"+color);
    }

}

this的使用

调用当前对象的成员属性 

public class ThisTest {
    public static void main(String[] args) {
        Person lsc=new Person("刘颂成","男",22);
        lsc.print();
    }
}
class Person{
    private String name;
    private String sex;
    private int age;
    public Person(String name,String sex,int age){
        this.name=name;
        this.age=age;
        this.sex=sex;
        System.out.println("Person的有参构造");
    }
    public void print(){
        System.out.println("姓名为"+name+"性别为"+sex+"年龄为"+age);
    }
}

注意点

  • 为什么在print方法里,name表示的是Person的成员变量name,而在Person构造方法里name表示是Person构造方法的形参name——就近原则,程序设计的概念,编译器会找到最近的相同名称的变量进行匹配
  • this.变量,代表的是当前类的成员变量,用this来打破就近原则

this调用当前对象的方法

this调用类的成员方法

public class ThisTest {
    public static void main(String[] args) {
            Person person=new Person();
            person.fun();
    }
}
class Person{
    private String name;
    private String sex;
    private int age;
    public void test(){
        System.out.println("Person类的test成员方法");
    }
    public void fun(){
        this.test();
        test();
        System.out.println("Person类的fun成员方法");
    }
}

  •  this.方法表示当前引用当前类的的成员方法
  • 前提是在一个类中,调用相同类中的方法 为什么this.test()和test(),他两的功能一样,因为test(),在编译后会变成this.test();

                                                  这Person编译后的代码

 this表示构造函数之间的相互调用

若不同参数的构造方法之间出现了重复的调用,可以用test(参数)来调用其他的构造方法

public class ThisTest {
    public static void main(String[] args) {
            Person per2=new Person("刘颂成","男",22);
    }
}
class Person{
    private String name;
    private String sex;
    private int age;
    public Person(){
        //Person的无参构造函数
        System.out.println("*******************");
    }
    public Person(String name){
        this();
        System.out.println("*******************");
        this.name=name;
    }
    public Person(String name,String sex){
        this(name);
        System.out.println("*******************");
        this.sex=sex;
    }
    public Person(String name,String sex,int age){
        this(name,sex);
        System.out.println("*******************");
        this.age=age;
    }
}

 注意点

  • this(参数)调用构造方法时,必须放在第一行
  •  this(参数)调用构造方法时,不能形成回环,否则会造成死循环

 this表示当前对象的引用

了解即可,先阶段知道原理即可

public class ThisTest {
    public static void main(String[] args) {
            Person per1=new Person();
            System.out.println(per1);
            per1.whoAmI();
             Person per2=new Person();
             System.out.println(per2);
             per2.whoAmI();

    }
}
class Person{
    private String name;
    private String sex;
    private int age;
    public Person(){
        System.out.println("*********************");
    }
    public void whoAmI(){
        System.out.println(this);
    }
}

这里的用法是当前的属性或者方法是哪个对象调用的,那么这个this就是指待这个对象 

代码块 

代码块指的就是使用{}括起来的一段代码,称为代码块

普通代码块 

定义在方法中,用{}括起来的代码段

成员代码块

定义在类中,用{}括起来的代码块

 说明构造块的执行是优于构造函数

静态代码块

定义在类中,使用static修饰的代码块,在类加载时执行一次

 实例化了Animal类的两个对象,但是只执行了一次静态构造块

————————————————————————————————————————

我们发现静态块的执行先于主方法

————————————————————————————————————————

编译后的代码 

我们发现在编译后,会将静态代码块的内容合并在一起

 ————————————————————————————————————————

 为什么age是100?

  • age是类变量,存于方法区,类定义的时候类变量就有初始值为0,这个类变量放入方法区(这时候只是类定义,并没有把类加载到内存)
  • 当主方法使用了Animal,就需要把Animal从方法区加载到内存——>类加载,静态代码块是在类加载执行,先执行a=10,然后执行a=100

————————————————————————————————————————

梳理一下类的内存执行顺序

匿名对象 

匿名就是表示没有名字的对象

  • 没有引用的对象就叫匿名对象
  • 匿名对象只能在创建对象时使用
  • 如果一个对象只使用一次,就不用了,可以考虑匿名对象

toString方法 

class Person { 
 private String name; 
 private int age; 
 public Person(String name,int age) { 
 this.age = age; 
 this.name = name; 
 } 
 public void show() { 
 System.out.println("name:"+name+" " + "age:"+age); 
 } 
} 
public class Main { 
 public static void main(String[] args) { 
 Person person = new Person("caocao",19); 
 person.show(); 
 System.out.println(person); 
//我们发现这里打印的是一个地址的哈希值 原因:调用的是Object的toString方法
 } 
}
class Person { 
 private String name; 
 private int age; 
 public Person(String name,int age) { 
 this.age = age; 
 this.name = name; 
 } 
 public void show() { 
 System.out.println("name:"+name+" " + "age:"+age); 
 } 
 //重写Object的toString方法
 public String toString() { 
 return "Person{" +  "name='" + name + '\'' + ", age=" + age +  '}'; 
 } 
} 
public class Main { 
 public static void main(String[] args) { 
 Person person = new Person("caocao",19); 
 person.show(); 
 System.out.println(person); 
//打印Person{name=caocao,age=19}
 } 
}

注意点

  • toString方法会在println时自动调用
  • toString Object 类提供的方法 , 我们自己创建的 Person 类默认继承自 Object , 可以重写 toString 方法实现我们自己版本的转换字符串方法. ( 关于继承和重写这样的概念 , 我们后面会重点介绍)

内部类 

其内部类就是将一个类嵌套到另一个类的内部的操作(将内部类当作类的一个普通属性看待),类比生活,心脏和人的关系,心脏也有很多属性和方法,也是一个类,那心脏相对于人就是一个内部类

为什么引入内部类

内部类的设计其实也是一种封装思想,其封装思想就是为了保护和易用

  • 内部类和外部类能方便访问彼此的私有域(属性和方法)
  • 内部类可以对外部类的外部完全隐藏(把内部类当作外部类的属性使用),可以使用private来修饰
  • 内部类可以变相实现多继承(不推荐) 

内部类和外部类能互相访问彼此的私有域 ,内部类能用private修饰

四大内部类

成员内部类

  • 在外部类的内部不使用static关键字定义的内部类就是成员内部类,成员内部类需要依赖外部类对象,先有外部类对象,然后有内部类对象

两种产生内部类对象的方式

  • 在外部类的内部产生成员内部类的对象
  • 若内部类对外部是可见的,不是private修饰内部类,在外部类的外部产生,外部类.内部类 内部类引用名称=new 外部类().new 内部类();

 关于内部成员成员类访问和创建问题

  •  成员方法可以访问类的实例变量和静态变量,但是不能定义静态变量
  • 静态方法只能访问静态变量,不可以访问成员变量 类比内部成员类
  • 内部成员类是可以访问其类中的静态域的(因为成员内部类是依赖于外部类对象的,都有了外部类对象,肯定能访问静态属性,成员属性也是可以访问的),但是不能在内部类定义静态域(假设可以,在成员内部类中拥有静态域,那么静态域是不需要对象就能访问的,都不需要成员内部类对象,那肯定不需要外部类对象,就违背了成员内部类要依赖外部类对象才能产生)

静态内部类

使用static关键字定义的在另一个类的内部类(类似于静态方法和属性),其和成员内部类最大的不同就是内部类不需要外部类的对象,和外部类是一个相对独立的关系

  • 静态内部类只能访问外部类的静态域,没有外部类对象,无法访问外部类的成员域
  • 静态内部类可以定义自己的成员域,静态内部类就是一个普通类,只不过套在一个类的内部罢了

方法内部类

方法内部类就是定义在方法中的内部类

  • 不能使用任何的权限修饰符(因为出了这个方法就不存在了,用修饰符没有意义)
  • 对外部类的外部完全隐藏
  •  Test内部类要使用fun方法的形参或者是局部变量,该变量必须是隐式的final声明
  • 在方法内部类也无法定义静态域,因为方法中就无法定义静态域,何况方法内部类被当作方法的局部变量
  • 在方法内部类中使用了一个变量,那么这个变量编译之后就相当于final 修饰这个变量

方法内部类只能使用外部方法的形参和局部变量,但是无法修改,若在内部类使用了这个变量那么在内部类或者方法中都是不能修改的

匿名内部类

一般定义在方法中的(形参和实参),就是没有任何修饰权限符,甚至连个类名称都没有的内部类,匿名内部类也是方法内部类的一种

  • 我们知道一个接口是无法实例化对象的,因此我们匿名内部类的实参新建的是一个IMessage是接口的子类,只不过这个子类只在fun方法中使用一次 
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

库里不会投三分

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值