Java的内部类与常用类

内部类

  • 在一个类的内部再定义一个完整的类,假如有两个类,body和head,head是不是该定义在body内?

package com.qf.demo;
//身体
public class Body{
   private String name;
   //头
   class Head{
       //内部类也会生成class文件
       public void show(){
           System.out.println(name);
      }
  }
}
  • 特点

    • 编译之后可以生成独立的字节码(Outer$Inner.class)文件

    • 内部类可以直接访问外部类的私有成员而不破坏封装

    • 可为外部类提供必要的内部功能组件

成员内部类

  • 在类的内部定义,与实例变量(属性/字段)、实例方法同级别的类。

  • 外部类的一个实例部分,创建内部类对象时,必须依赖外部类对象。

    • Outer out = new Outer();

    • Inner inner = out.new Inner();

  • 当外部类、内部类存在重名属性时,会优先访问内部类属性。

  • 成员内部类不能定义静态成员,但是可以定义静态常量。


package com.qf.demo_0;
//外部类
public class Outer{
   //实例变量
   private String name = "张三";
   private int age = 20;
   //内部类
   class Inner{
       private String address = "beijing";
       private String phone = "12315";
       //内部类属性和外部类属性名字相同,访问外部类属性时必须写上外部类名.this。
       private String name = "李四";
       private static final String country = "china";
       //方法
       private void show(){
           //打印外部类属性
           System.out.println(Outer.this.name);
           System.out.println(name);
           System.out.println(age);
           //打印内部类属性
           System.out.println(address);
           System.out.println(phone);
           //
      }
  }
}

package com.qf.demo_1;

import com.qf.demo_0
public class Test{
   public static void main(String[] args){
       //创建一个外部类对象
       Outer outer = new Outer();
       //创建内部类对象,要先写上外部类的对象再去new那个Inner。
       Inner inner = outer.new Inner();
       
       //一步到位的写法
       //Inner inner = new Outer().new Inner();
       inner.show();
  }
}

静态内部类

  • static表示类的属性或者方法事,可直接用类名来调用属性或方法。

  • 不依赖外部类对象,可直接创建或通过类名访问,可声明静态成员。

  • 用法相当于一个外部类,普通的一个类可以包含的它都能包含。

  • 静态内部类在外部类里面的原因只是为了给外部类提供一些功能。

  • 只能直接访问外部类的静态成员(实例成员需要实例化外部类对象)

    • Outer.Inner inner = new Outer.Inner();

    • Outer.Inner.show();


package com.qf.demo;
//外部类
public class Outer{
   private String name = "hua";
   private int age = 18;
   //静态内部类和外部类同级别
   static class Inner{
       private String address = "shanghai";
       private String phone = "111";
       //静态成员
       private static int count = 1000;
       public void show(){
           //调用外部类的属性和方法
           //先创建外部类对象
           Outer outer = new Outer();
           //调用外部类对象属性
           System.out.println(outer.name);
           System.out.println(outer.age);
           //调用静态内部类的属性和方法
           System.out.println(address);
           System.out.println(phone);
           //调用静态内部类的静态属性
           System.out.println(Inner.count);
      }
  }
}

package com.qf.demo;

public class Test{
   public static void main(String[] args){
       //直接创建静态内部类对象就可以,此处没有new外部内的对象,只是为了显示一个包含关系。
       Outer.Inner inner = new Outer.Inner(); //如果Outer后面加了括号就表示创建对象,没有只是代表包含关系。
       //调用方法
       inner.show();
  }
}
  • 普通的类是不能用static修饰符的,只有内部的类才可以是用static修饰的静态内部类。

局部内部类

  • 局部变量是定义在方法里面的,局部内部类和局部变量一样。

  • 定义在外部类的方法中,作用范围和创建对象范围仅限于当前方法。

  • 局部内部类访问外部类当前方法中的局部变量时,因无法保障变量的生命周期与自身相同,变量必须修饰为final。

  • 限制:局部内部类的使用范围为只能在其所属的方法中使用

package com.qf.demo;
//外部类
public class Outer{
   private String name = "jerry";
   private int age = 20;
   
   public void show(){
       //定义局部变量,注意局部变量不能带访问修饰符。
       //final String address = "shenzhen";
       String address = "shenzhen";
       //局部内部类,注意局部内部类也不能带访问修饰符。
       class Inner{
           //局部内部类的属性
           private String phone = "123";
           private String email = "12315@qq.com";
           //局部内部类中不能加静态成员
           //如private static int count = 2;就会报错
           //但是可以加静态常量
           private final static int count = 2000;
           
           public void show2(){
               //访问外部类的属性,与局部变量级别相同,可访问。
               System.out.println(name);
               System.out.println(age);
               //访问内部类的属性
               System.out.println(phone);
               System.out.println(email);
               
               //访问局部变量,jdk1.7时要求该变量必须是常量,1.8后jdk会自动添加final。
               System.out.println(address); //但后面也就不能再对address做修改了。
          }
      }
       //要访问局部内部类,只有再在局部内部类所属方法下创建局部内部类的对象。
       Inner inner = new Inner();
       //方法执行完后,它的局部变量address会立即消失,类创建的变量inner也会立即消失,但是new出来的类对象在堆里就不会立即消失;并且这个class Inner这个类因为在堆里所以也不会立即消失,所以就出现了变量address没有了,但是类Inner还在的情况,因为类不能引用已经消失的变量,所以得给它加个final把它变成常量放到堆的方法区中去,此时的address只是常量"shenzhen"的引用罢了。
       inner.show2();
  }
}

  • 静态方法不能访问非静态属性,因为静态方法随类初始化,但没有创建类对象时是没有成员属性的。


package com.qf.demo;

public class Test01{
   public static void main(String[] args){
       Outer outer = new Outer();
       outer.show();
  }
}

匿名内部类

  • 没有类名的局部内部类(一切特征都与局部内部类相同),其实它的名字不是编写的人定义的,而是编译器在编译时自动定义的。

  • 必须继承一个父类或者实现一个接口

  • 定义类、实现类、创建对象的语法合并,只能创建一个该类的对象。

  • 优点:减少代码量;缺点:可读性较差。


package com.qf.demo;
//定义接口
public interface Usb{
   //服务
   void service();
}

package com.qf.demo;

public class Mouse implements Usb{
   @Override
   public void service(){
       System.out.println("link successed!");
  }
}

package com.qf.demo;

public class TestUsb{
   public static void main(String[] args){
       //创建接口类型的变量
       Usb usb = new Mouse();
       usb.service();
       
       //局部内部类
       class Fan implements Usb{
           @Override
           public void service(){
               System.out.println("work");
          }
      }
       //使用局部内部类创建对象,但是只用一次就不用了,写得复杂比较浪费,简化一下就有了匿名内部类。
       Usb usb = new Fan();
       usb.service();
       
       //匿名内部类(相当于创建了一个局部内部类)
       Usb usb = new Usb(){
           @Override
           public void service(){
               System.out.println("work");
          }
      };
       usb.service();
  }
}

常用类

Object类

  • 超类、基类,所有类的直接或间接父类,位于继承树的最顶层。

  • 任何类,如果没有书写extends显示继承某个类,都默认直接继承Object类,否则为间接继承。

  • Object类中所定义的方法,是所有对象都具备的方法。

  • Object类型可以存储任何对象

    • 作为参数,可以接受任何对象。

    • 作为返回值,可以返回任何对象。

getClass()方法
  • public final Class<?> getClass(){}

  • 返回引用中存储的实际对象类型

  • 应用:通常用于判断两个引用中实际存储对象类型是否一致。


package com.qf.demo;

public class Student{
   private String name;
   private int age;
   
   public Student(){
       
  }
   public Student(String name, int age){
       super();
       this.name = name;
       this.age = age;
  }
   
   public String getName(){
       return name;
  }
   public void setName(String name){
       this.name = name;
  }
   public int getAge(){
       return age;
  }
   public void setAge(int age){
       this.age = age;
  }
}

package com.qf.demo;

public class TestStudent{
   public static void main(String[] args){
       //getClass方法
       Student s1 = new Student("aaa", 20);
       Student s2 = new Student("bbb", 22);
       //判断s1和s2对象是否是同一个类型
       Class class1 = s1.getClass();
       Class class2 = s2.getClass();
       
       if(class1 == class2){
           System.out.println("the same type");
      }else{
           System.out.println("none");
      }
       
       //hashCode方法
       System.out.println("******hash*******");
       System.out.println(s1.hashCode());
       System.out.println(s2.hashCode());
       
       Student s3 = s1;
       System.out.println(s3.hashCode());
  }
}

hashCode()方法
  • public int hashCode(){}

  • 返回该对象的哈希码值

  • 哈希值是根据对象的地址或字符串或数字使用 hash 算法计算出来的 int 类型的数值

  • 一般情况下相同对象返回相同的哈希码


toString方法
  • public String toString(){}

  • 返回该对象的字符串表示(表现形式)默认返回该方法对象类型以及十六进制的 hash 值

  • 可以根据程序需求覆盖该方法,如:展示对象各个属性的值。


//toString方法
System.out.println("******toString*******");
System.out.println(s1.toString());
System.out.println(s2.toString());
//com.qf.demo.Student@6d06d69c

//重写toString方法(方法名、参数列表、返回值和父类一致,而且访问修饰符不能比父类的更加严格)
@Override
public String toString(){
   return name + ":" + age;
}
//ALT + ENTER + S
@Override
public String toString(){
   return "Student [name=" + name + ", age=" + age +"]";
}

equals()方法
  • public boolean equals(Object obj){}

  • 默认实现为(this == obj),比较两个对象地址是否相同。

  • 可进行覆盖,比较两个对象的内容是否相同。


//4.equals方法,判断两个对象是否相等。
System.out.println(s1.equals(s2));
Student s4 = new Student("xiaoming", 17);
Student s5 = new Student("xiaoming", 17);
System.out.println(s4.equals(s5)); //因为new会新开地址,new出来的对象即使内容一样,地址不一样用equals也会判断为false。

  • equals()方法覆盖(重写)的步骤

    • 比较两个引用是否指向同一个对象

    • 判断obj是否为null

    • 判断两个引用指向的实际对象类型是否一致(可用getClass、instanceof等方法)

    • 强制类型转换

    • 依次比较各个属性值是否相同


@Override
public boolean equals(Object obj){
   //return super.equals(obj);
   //1.判断两个对象是否是同一个引用
   if(this == obj){
       return true;
  }
   //判断obj是否为null
   if(obj == null){
       return false;
  }
   //是否是同一个类型
   //if(this.getClass() == obj.getClass()){}
   if(obj instanceof Student){
       //4.强制类型转换
       Student s = (Student)obj;
       //5.比较属性
       if(this.name.equals(s.getName()) && this.age == s.getAge()){
           return true;
      }
  }
   return false;
}

finalize()方法
  • 当对象被判定为垃圾对象时,由 JVM 自动调用此方法,用以标记垃圾对象,进入回收队列。

  • 垃圾对象:没有有效引用指向此对象时,为垃圾对象。

  • 垃圾回收:由GC销毁垃圾对象,释放数据存储空间。

  • 自动回收机制:JVM 的内存耗尽,一次性回收所有垃圾对象。

  • 手动回收机制:使用 System.gc();通知 JVM 执行垃圾回收。


@Override
protected void finalize() throws Throwable{
   System.out.println(this.name + "对象被回收了");
}

package com.qf.demo;

public class TestStudent{
   public static void main(String[] args){
       Student s1 = new Student("aaa", 12);
       Student s1 = new Student("bbb", 15);
       Student s1 = new Student("ccc", 18);
       Student s1 = new Student("ddd", 20);
       Student s1 = new Student("eee", 21);
       new Student("a", 12);
       new Student("b", 15);
       new Student("c", 18);
       new Student("d", 20);
       new Student("e", 21);
       //告诉 JVM 来回收垃圾
       //前五个不被回收,后五个被回收,因为前五个有栈中的变量接收堆中new出来的对象,这个变量还是在main函数中运行着的;后五个只是堆中对象,没有引用就等于是闲置,所以被当成垃圾了。
       System.gc();
       System.out.println("回收垃圾");
  }
}

包装类

  • 基本数据类型中对应的引用数据类型就叫包装类

  • 基本类型数据在栈里,引用类型数据在堆里。

  • Object 可统一所有数据,包装类的默认值是null。


基本数据类型包装类型
byte Byte
short Short
int Integer
long Long
float Float
double Double
boolean Boolean
char Character

  • 类型转换与装箱、拆箱(基本类型与包装类型间的转换)

    • 装箱:把(变量)基本类型转换为对象(引用类型),从栈拿去堆。

  • 8种包装类提供不同类型间的转换方式:

    • Number 父类中提供的6个共性方法

    • parseXXX() 静态方法(XXX代表一个int、float、double类型)

    • valueOf() 静态方法(把基本类型转换成引用类型)

  • 注意:需要保证类型兼容,否则要抛出 NumberFormatException 异常。


package com.qf.demo;

public classs Demo{
   public static void main(String[] args){
       //类型转换:装箱操作(把基本类型转成引用类型)
       int num1 = 18;
       //使用Integer类型创建对象
       Integer int1 = new Integer(num1);
       Integer int2 = Integer.valueOf(num1);
       System.out.println("装箱");
       System.out.println(int1);
       System.out.println(int2);
       
       //类型转换:拆箱(引用类型转为基本类型)
       Integer int3 = new Integer(100);
       int num2 = int3.intValue();
       System.out.println("拆箱");
       System.out.println(num2);
       
       //JDK1.5之后Java提供了自动装箱和拆箱功能
       int age = 30;
       //自动装箱,编译器完成转换工作。
       Integer int4 = age;
       System.out.println("自动装箱");
       System.out.println(int4);
       
       //自动拆箱,编译器完成转换工作。
       int age2 = int4;
       System.out.println("自动拆箱");
       System.out.println(int4);
  }
}

基本类型和字符串的转换

//基本类型转成字符串
int n1 = 100;
int n2 = 15;
//1.1使用 + 号
String s1 = n1 + "";
//1.2使用Integer中的toString()方法
String s2 = Integer.toString(n1);
String s3 = Integer.toString(n2, 16); //转换为十六进制输出
System.out.println(s1);
System.out.println(s2);
System.out.println(s3);

//字符串转换成基本类型
String str = "150";
//1.2使用Integer.parseXXX();
int n3 = Integer.parseInt(str);
System.out.println(n3); //使用parseXXX的时候字符串里不能出现非数字

//boolean字符串形式转成基本类型,只有字符串的“true”才能转换为true,其他的类型都只能转换为false。
String str2 = "true";
String str3 = "tttt";
boolean b1 = Boolean.parseBoolean(str2);
boolean b2 = Boolean.parseBoolean(str3);
System.out.println(b1);
System.out.println(b2);

整数缓冲区

  • Java 预先创建了 256 个常用的整数包装类型对象

  • 在实际应用当中,对已创建的对象进行复用,减小内存消耗。

package com.qf.demo;

public class demo2{
   public static void mian(String[] args){
       //面试题
       Integer int1 = new Integer(100);
       Integer int2 = new Integer(100);
       System.out.println(int1 == int2); //false
       
       Integer int3 = 100; //自动装箱
       //自动装箱调用Integer.valueOf()而不是使用的构造方法
       Integer int4 = 100;
       System.out.println(int3 == int4); //true
       
       Integer int5 = 200; //自动装箱
       Integer int6 = 200; //大于Java缓存区里预存的数值了,不能找对应已经存在的预存对象,只能重新new个对象,所以地址不一样了。
       System.out.println(int5 == int6); //false
  }
}

posted on 2021-12-30 22:04  愿将过往均储藏  阅读(94)  评论(0)    收藏  举报

导航