12.1 内部类
12.1.1 内部类的概念
内部类的概念:
- 在一个类中定义的类,称之为内部类(InnerClass),外面的类的称之为外部类(OutClass)
内部类的分类:
- 1、成员内部类
- 2、静态内部类
- 3、局部内部类
- 4、匿名内部类
内部类的特点:
- 1、内部类可以访问到外部类的私有成员,且不破坏封装性
- 2、内部类也会生成.class文件。名为:外部类名$内部类名.class
外部类的特点:
- 一个java 文件中可以编写多个类,但是只能有一个类能使用public修饰,称之为主类。主类名必须与文件名一致
- 建议:以后开发中,一个java文件就编写一个类
public class OutClass {//外部类 //内部类 public class InnerClass{ } }
12.1.2 成员内部类
成员内部类
- 1、成员内部类中只能定义非静态属性和方法
- 2、成员内部类中可以访问外部类的成员,私有的可以,静态的也可以
成员内部类如何创建对象
- 语法: 内部类 对象名 = new 外部类().new 内部类();
public class OuterClass1 { String name; private String hobby; static int age; public void show() { System.out.println("show()"); } public class InnerClass{ //定义属性 int a ; //static int b; //错误 ,成员内部类中不能定义静态变量 //定义方法 public void m1() { System.out.println("成员内部类的成员变量"+a); System.out.println("外部类的成员变量"+name); System.out.println("外部类的私有成员变量"+hobby); System.out.println("外部类的静态变量"+age); } // public static void m2() {//错误 成员内部类中不能定义静态方法 // } } }
12.1.3 静态内部类
静态内部类
- 1、静态内部类中可以定义属性和方法,也能定义静态属性和方法
- 2、静态内部类中只能访问静态外部类的静态属性和方法
静态内部类创建对象
- 语法:内部类 对象名 = new 外部类.内部类();
public class OuterClass2 { String name; static int age; private static String hobby; public static class InnerClass2{ //定义属性 int a; static int b; //定义方法 public void m1() { //System.out.println("外部类的成员变量"+name); System.out.println("外部类的静态变量"+age); System.out.println("外部类的私有静态变量"+hobby); } public static void m2() { } } }
12.1.4 局部内部类
局部内部类: 定义在方法中
特点:
- 1、成员内部类中只能定义非静态属性和方法
- 2、成员内部类中可以访问外部类的成员,私有的可以,静态的也可以
- 3、局部内部类只能在方法内创建对象
面试题:
局部内部类只能访问局部常量。
在jdk1.7版本中,如果局部变量在局部内部类中使用必须要显式的加上final
在jdk1.8版本中,final是默认加上的
因为局部变量在方法结束后,就会被销毁,而局部内部类的对象却要等到内存回收机制进行销毁所以如果是常量的话,那么常量就会被存放在常量池中,
public class OuterClass4 { String name; private int age; static String hobby; public void show() { int num = 10; class InnerClass4{ public void m1() { System.out.println("外部类的成员变量"+name); System.out.println("外部类私有的成员变量"+age); System.out.println("外部类静态变量"+hobby); //num = 20; System.out.println("访问局部变量"+num); } //static int b; //public static void m2() {} } InnerClass4 in = new InnerClass4(); in.m1(); } }
2.1.5 匿名内部类
匿名内部类:本身就是一个对象
语法:
new 父类(){
重写父类的方法
}
特点:
- 1、匿名内部类本身就是一个对象
- 2、一般在匿名内部类中不会定义属性和方法,因为没有意义
- 3、匿名内部类的父类一般都是抽象类或者是接口
匿名内部类的应用场景
- 如果一个方法的参数是接口,且这个接口只需要实现一次,那么就可以使用匿名内部类
- 这个接口的实现每一次都不相同,就可以使用匿名内部类
Father f = new Father() { int a; public void m1() { System.out.println("m1()..."); } };
匿名内部类的应用场景
public class Test { public static void main(String[] args) { //使用匿名内部类作为参数传递 gdbh(14,new MyInterface() { @Override public boolean isZS(int num) { for (int i = 2; i < num; i++) { if(num % i == 0) { return false; } } return true; } }); } public static void gdbh(int num,MyInterface mi) { for (int i = 3; i <= num/2; i++) { if(mi.isZS(i) && mi.isZS(num-i)) { System.out.println(num+"="+i+"+"+(num-i)); } } } } interface MyInterface{ boolean isZS(int num); }
12.1.6 内部类中this用法
this表示当前类对象
- 如果有内部类,那么this在哪个类中使用就是指的哪个类的当前类的对象
- 如果要想调用外部类的属性和方法,那么需要使用外部类名.this.属性或者方法名
public class OuterClass3 { String name = "张三"; public class InnerClas3{ String name ="李四"; public void m1() { String name = "王五"; System.out.println("方法中的name"+name); System.out.println("内部类中的name"+this.name); //this关键字表示的时候当前类的对象,如果在内部类中this表示的是内部类的对象, //如果想要表示外部类的对象,外部类名.this System.out.println("外部类中的name"+OuterClass8.this.name); } } }
12.2 Object类
12.2.1 概念
Object 类
- 1、Object 是所有的类的超类、基类。位于继承树的最顶层。
- 2、任何一个没有显示定义extends父类的类。都直接继承Object,否则就是间接继承
- 3、任何一个类都可以享有Object提供的方法
4、Object类可以代表任何一个类(多态),可以作为方法的参数、方法的返回值
12.2.2 Object中常用方法
12.2.2.1 getClass方法
此方法用于返回该对象的真实类型(运行时的类型)
- public final Class<?> getClass()
//判断运行时d对象和c对象是否是同一个类型 Animal d = new Dog(); Animal c = new Cat(); //方式1:通过 instanceof 关键字判断 if((d instanceof Dog && c instanceof Dog) ||(d instanceof Cat && c instanceof Cat)) { System.out.println("是同一个类型"); }else { System.out.println("不是同一个类型"); } //方式2:通过getClass方法 判断 if(d.getClass() == c.getClass()) { System.out.println("是同一个类型"); }else { System.out.println("不是同一个类型"); }
12.2.2.2 hashCode方法
public native int hashCode();
- 1、返回该对象的十进制的哈希吗值
- 2、hash值是由hash算法通过对象的地址、对象中的字符串、数字等,计算出来的
- 3、相同的对象应当返回相同的哈希吗值,不同的对象尽量返回不同的哈希码值
Student stu1 = new Student("zhangsan", 30); Student stu2 = new Student("zhangsan", 30); Student stu3 = stu1; System.out.println(stu1.hashCode()); System.out.println(stu2.hashCode()); System.out.println(stu3.hashCode()); //hash突出 String str1 = "通话"; String str2 = "重地"; System.out.println(str1.hashCode()); System.out.println(str2.hashCode());
12.2.2.3 toString方法
返回对象的字符串表现形式
- 全限定名+@+十六进制的hash值(地址)
- 如果直接输出一个对象,那么默认会调用这个对象的toString方法,而toString方法是Object类提供的,返回的是“对象的地址”。但是我们一般输出对象希望输出的是对象的属性信息,所以可以重写父类的toString方法
@Override public String toString() { return "Student [name=" + name + ", age=" + age + "]"; }
12.2.2.4 equals方法
Object类的equals方法的作用是比较两个对象是否相等。比较的是内存地址。其底层代码的是==
如果不想比较内存地址,那么需要重写equals方法
Student stu1 = new Student("zhangsan", 30); Student stu2 = new Student("zhangsan", 30); //重写equals方法之前 System.out.println(stu1 == stu2); //false System.out.println(stu1.equals(stu2));//false //希望如果两个对象的属性一样,就认为两个对象是相同的对象 //重写equals方法之后 System.out.println(stu1 == stu2); //false System.out.println(stu1.equals(stu2));//true System.out.println(stu1.hashCode()); System.out.println(stu2.hashCode());
重写equals方法
public boolean equals(Object obj) { //1、非空判断 if(obj == null) { return false; } //2、如果当前对象与obj相等 if(this == obj) { return true; } //3、判断obj是否属于Student类型 if(obj instanceof Student) { Student stu = (Student)obj; //4、判断属性 if(this.name.equals(stu.name) && this.age == stu.age) { return true; } } return false; }
总结:== 和 equals的区别
- 两个东西都是用于比较的
- == 可以用于基本类型和引用类型
- ==在基本类型的比较中,比较的值是否相等,如果相等返回true,否则返回false
- ==在引用类型的比较中,比较的地址是否相等,如果相等返回true,否则返回false
equals只能用于引用类型的比较
- equals方法是Object类提供的方法,其底层实现是==比较,所以在没有重写父类的equals方法时。比较的也是地址。如果希望两个对象的属性一样,就认为两个对象是相同的对象,那么需要重写equals方法,但是重写了equals的同时也需要重写hashcode方法,因为java中约定两个对象相等,那么两个对象的hash值也应该相等
12.2.2.5 finalize方法
当垃圾回收器回收垃圾对象的时候,自动调用
public class Test5 { public static void main(String[] args) { Person p = new Person(); //手动将对象标记为垃圾对象 p = null; //触发垃圾回收器,回收垃圾对象 System.gc(); } } class Person{ @Override protected void finalize() throws Throwable { super.finalize();//不要删除 System.out.println("finalize方法执行了"); } }
12.3 包装类
12.3.1 概念
为什么要有包装类
- 因为基本数据类型不具有方法和属性。而引用数据类型可以拥有属性和方法,使用更加的灵活
- 所以Java给8种基本数据类型提供对应8个包装类。包装类也就是引用数据类型
基本类型 | 包装类型 |
byte | Byte |
short | Short |
int | Integer |
long | Long |
float | Float |
double | Double |
char | Character |
boolean | Boolean |
12.3.2 装箱和拆箱
装箱就是将基本类型转换成包装类
拆箱就是将包装类型转换成基本类型
jdk1.5 之前装箱和拆箱
//在jdk1.5之前 拆装箱的过程 byte b = 10; //装箱 Byte b1 = Byte.valueOf(b); System.out.println(b1); //拆箱 byte b2 = b1.byteValue(); System.out.println(b2);
jdk1.5 之后的装箱和拆箱
//在jdk1.5之后 拆装箱的过程 int i = 10; //装箱 Integer i1 = i; System.out.println(i1); //装箱 int i2 = i1; System.out.println(i2);
12.3.3 Number类
Byte、Short、Integer、Long、Float、Double六个子类
提供一组方法,用于将其中某一种类型转换成其他类型 xxxValue()方法
Integer a = 100; Byte b = a.byteValue(); Short c = a.shortValue(); Long d = a.longValue(); Float e = a.floatValue(); Double f = a.doubleValue(); Integer g = a.intValue();
12.3.4 常用的包装类
Integer 、Double
12.3.4.1定义方式
//Integer、Double的定义方式 Integer ii1 = new Integer(100); //或者 Integer ii1 = 100; Double dd1 = new Double(100.2); //或者 Double dd1 = 100.2;
12.3.4.2 常用的属性
System.out.println(Integer.MAX_VALUE); System.out.println(Integer.MIN_VALUE); System.out.println(Double.MAX_VALUE); System.out.println(Double.MIN_VALUE);
12.3.4.3 常用的方法
将字符串类型的数值转换成int或者是double类型
//常用方法: //将字符串转换成int或者是double String s = "123"; //方式1: int i = Integer.parseInt(s); System.out.println(i); //方式2: int i2 = Integer.valueOf(s); System.out.println(i2); String s1 = "20.5"; //方式1: double d = Double.parseDouble(s1); System.out.println(d); //方式2: double d1 = Double.valueOf(s1); System.out.println(d1); //java.lang.NumberFormatException 数字格式化异常 String s2 = null; System.out.println(Integer.parseInt(s2));
12.3.5 缓冲区(面试题)
public class Demo02 { public static void main(String[] args) { /** * 面试题:整数型包装类缓冲区 * 整数型的包装类定义缓冲区(-128~127),如果定义的数在这个范围你之内,那么直接从缓存数组中获取, * 否则,重新new新的对象 */ Integer i1 = new Integer(10); Integer i2 = new Integer(10); System.out.println(i1 == i2); //false System.out.println(i1.equals(i2));//true Integer i3 = 1000; //Integer i3 = new Integer(1000); Integer i4 = 1000; //Integer i3 = new Integer(1000); System.out.println(i3 == i4); //false System.out.println(i3.equals(i4));//true Integer i5 = 100; //IntegerCache.cache[i + (-IntegerCache.low)] Integer i6 = 100; //IntegerCache.cache[i + (-IntegerCache.low)] System.out.println(i5 == i6);//true System.out.println(i5.equals(i6));//true } }