目录
内部类
1.1 内部类
1.1.1 概述
如果一个事物的内部包含另一个事物,那么这就是一个类的内部包含另一个类。
1.1.2 成员内部类
在一个类的内部定义一个类,位置和成员变量以及成员方法持平。分为静态内部类和非静态内部类,前者在class前用static修饰。
1.1.2.1 静态内部类
格式
修饰符 class 外部类名称{
修饰符 static class 内部类名称{…}
…
}
使用:
外部类名称.内部类名称 对象名 = new 外部类名称.内部类名称();
1.1.2.2 非静态内部类
格式
修饰符 class 外部类名称{
修饰符 class 内部类名称{…}
…
}
使用:
外部类名称.内部类名称 对象名 = new 外部类名称().new 内部类名称();
1.1.2.3 静态内部类和非静态内部类的区别
-
静态内部类只能访问外部类的静态成员和方法;而非静态内部类可以访问外部类的所有成员和方法,包括静态和非静态的。
-
静态内部类的创建不依赖于外部类的实例;而非静态内部类需要通过依赖外部类的实例来创建。
-
静态内部类和外部类之间没有直接的引用关系;而非静态内部类可以直接访问外部类的成员。在非静态内部类中,可以使用外部类的引用来访问外部类的成员,例如外部类名.this.成员名。
-
静态内部类可以在没有外部类实例的情况下被单独实例化和使用;而非静态内部类必须在外部类的实例上使用。
1.2.3.4 案例 (非静态内部类)
例如:有一个学生类对象,在内部有存在一个学生成绩的内部类(统计学生各科的成绩)。这种和每个学生独立捆绑的内部类,只有当学生对象存在的时候才有意义。
源代码:
package innerClass;
public class Student {
/**
* 姓名
*/
String name;
/**
* 年龄
*/
int age;
/**
* 性别
*/
char sex;
/**
* 年级
*/
static String gradeString;
/**
* 非静态内部类:封装学生的成绩,只有存在学生对象的时候才有意义,每个对象互不影响。
* @author Administrator
*
*/
public class Score{
/**
* 语文
*/
double language;
/**
* 数学
*/
double mathematics;
/**
* 英语
*/
double english;
public Score(double language,double mathematics,double english) {
this.language = language;
this.mathematics = mathematics;
this.english = english;
}
/**
* 成绩等级
*/
public void grade() {
double average = (language + mathematics + english) / 3;
if(average >= 85 && average <= 100) {
System.out.println(name + "的成绩等级为A!");
}else if(average >= 70 && average < 85) {
System.out.println(name + "的成绩等级为B!");
}else if(average >= 60 && average < 70) {
System.out.println(name + "的成绩等级为C!");
}else if(average < 60) {
System.out.println(name + "的成绩等级为D!");
}
}
/**
* 打印年级
*/
public void printGrade() {
//非静态内部类:可以访问外部类的所有属性和方法,包括静态和非静态
System.out.println(name + "是" + gradeString + "年级的学生!");
}
}
/**
* 主函数:程序入口
* @param args
*/
public static void main(String[] args) {
Student student = new Student();
student.name = "张三";
//静态属性直接使用 类名.属性名 访问
Student.gradeString = "高二";
//因为成绩要在学生对象存在才有意义,所以其实例化必须建立在一个外部类对象的基础之上
Score score = student.new Score(89.1,94.5,70);
score.grade();
score.printGrade();
}
}
输出结果:
1.1.3 局部内部类
局部内部类指写在方法内部的类,作用域只在当前方法中,出了方法就不能被使用了。
格式:
修饰符 class 外部类名称{
修饰符 返回值类型 外部类方法名称(参数列表){
class 局部内部类名称{…}
}
}
使用:就是调用外部类的一个方法。
外部类 对象名 = new 外部类(); 对象名.外部类方法名();
注意事项:
如果希望访问所在方法的局部变量,那么这个局部变量必须是final修饰的,但只要局部变量只赋值一次,那么final关键字可以省略,编译器会默认加上,所以再次赋值会报错。
原因:
1.new出来的对象在堆内存当中,局部变量是跟着方法走的,在栈内存当中。
2.方法运行结束之后,立即出栈,局部变量就会消失,但是new出来的对象会在堆当中持续存在,直到垃圾回收消失,所以比局部变量的生命周期长,必须保证局部变量是唯一不变的。
1.1.4 匿名内部类
如果接口的实现类(或者父类的子类)只需要使用唯一的一次,那么这种情况下就可以省略掉该类的定义,而改为使用【匿名内部类】。
格式
接口名称 对象名 = new 接口名称(){
//覆盖重写所有的抽象方法
};
说明:
1.new代表创建对象的动作。
2.接口名称就是匿名内部类需要实现哪个接口(或抽象父类)。
3.标红区域才是匿名内部类的内容,分号不能少 。
package innerClass; /** * 匿名内部类:当一个类只是当前使用一次的时候,就可以使用匿名内部类。 * 好处: * 可以不用创建实现类,重写方法,然后再用多态的形式调用,节省代码。 * 坏处: * 当有多处需要使用到该方法时,每次都要重写该方法,造成代码冗余。 * 总之,有利有弊,根据不同场景合理使用,方能发挥其最大的作用。 * @author Administrator * */ public class AnonymousInnerClass { public static void main(String[] args) { /** * 匿名内部类,抽象父类形式 */ Animal animal = new Animal() { @Override public void eat() { System.out.println("动物进食!"); } }; animal.eat(); /** * 匿名内部类,接口形式 */ Pet pet = new Pet() { @Override public void play() { System.out.println("玩耍!"); } }; pet.play(); } }
注意事项:
1.匿名内部类,在【创建对象】的时候,只能使用唯一的一次。
2.匿名对象,在【调用方法】时,只能调用唯一的一次。
3.匿名内部类是省略了【实现类/子类的名称】,但是匿名对象是省略了【对象名称】,匿名内部类和匿名对象不是同一回事。
1.1.5 内部类的同名变量访问
- 局部变量:变量名;
- 内部类的成员变量:this.变量名;
- 外部类的成员变量:外部类名称.this.变量名;
1.1.6 权限修饰符
public > protected > (default) > private
- 外部类:能用public/(default)修饰。
- 局部内部类:什么都不能写。
- 成员内部类:能用public/protected/(default)/private修饰。
1.1.7 成员变量类型
- 用基本类型作为成员变量类型:private int age;
- 用类作为成员变量类型:private 自定义类名 变量名;
- 用接口作为成员变量类型:private 接口名 变量名;
1.1.8 接口作为方法的参数和返回值
Pet.java类
package innerClass;
public interface Pet{
/**
* 提供setName(String name) 为该宠物命名
* @param name
*/
public void setName(String name);
/**
* 提供 play()方法
*/
public void play();
}
Cat.java类
package innerClass;
/**
* 定义Cat类,实现Pet接口重写play/setName方法
*/
public class Cat implements Pet{
/**
* 该类必须包含String属性来存宠物的名字。
*/
private String name;
/**
* 实现接口的方法
*/
@Override
public void play() {
System.out.println(" 这只叫 " + name + " 的猫咪正在眨眨眼!");
}
/**
* 实现接口的方法
*/
@Override
public void setName(String name) {
this.name = name;
}
}
测试类:
package innerClass;
public class ParamsTest {
public static void methodTest(Pet pet) {
pet.setName("福福");
pet.play();
}
public static Pet methodTest2() {
Pet pet = new Cat();
pet.setName("年年");
pet.play();
return pet;
}
public static void main(String[] args) {
System.out.println("接口作为方法参数值:");
Pet pet = new Cat();
methodTest(pet);
System.out.println("接口作为方法返回值:");
Pet pet2 = methodTest2();
}
}
输出结果:
1.2 final关键字
1.2.1 含义
代表最终的、不可改变的。
1.2.2 常见用法
1.2.2.1 修饰一个类
格式:
public final class 类名称{…}
含义:
当前这个类不能有任何的子类,但是可以有父类。
注意事项:
一个类如果是final的,那么其中所有的成员方法都无法进行覆盖重写。
1.2.2.2 修饰一个方法
格式:
修饰符 final 返回值类型 方法名(){…}
含义:
当final关键字修饰一个方法的时候,这个方法就是最终方法,也就是不能覆盖重写。
注意事项:
对于类、方法来说,abstract关键字和final关键字不能同时使用,因为矛盾。
1.2.2.3 修饰一个局部变量
格式:
final 数据类型 局部变量名;
含义:
一旦使用final关键字用来修饰局部变量,那么这个变量就不能进行更改。
注意事项:
1.对于基本类型来说,不可变说的是变量当中的数据不可改变
2.对于引用类型来说,不可变说的是变量当中的地址值不可变,数据可以改变。
1.2.2.4 修饰一个成员变量
格式:
final 数据类型 成员变量名;
含义:
对于成员变量来说,如果使用final关键字修饰,那么这个变量也照样是不变的。
注意事项:
1.由于成员变量具有默认值,所以用了final之后必须手动赋值,不会再给默认值了。
2.对于final的成员变量,有下面两种赋值方式,二者只能选其一。
public class FinalPractice { /** * 1.声明变量时赋值 */ private final String name = "Annie"; }
public class FinalPractice { private final String name; /** * 2.构造方法赋值 * @param name */ public FinalPractice(String name) { this.name = name; } }
如果同时采用两种方式,会编译报错。
3.必须保证类当中所有重载的构造方法,都最终会对final的成员变量进行赋值。
1.3 静态关键字static
1.3.1 概述
static关键字是一个用于声明类成员(变量或方法)的修饰符。它可以与类的静态变量和静态方法一起使用。运用了static关键字,那么这样的内容不再属于对象自己,而是属于类的,所以凡是本类的对象,都共享同一份。
1.3.2 使用
1.3.2.1 修饰成员变量
格式:
private static String initAge;
说明:
1.表示这个变量是一个静态变量,也称为类变量。
2.静态变量在类被加载时就会被初始化,在整个程序运行过程中,只会有一份拷贝,所有类的实例对象共享同一个静态变量的值。
使用:
1.通过“类名.成员名”的方式访问,例如:Animal.initAge。
2.通过“对象名.成员名”的方式访问,例如:Animal a = new Animal; a.initAge;。
通常我们都是使用方式一,以便区分静态变量和对象变量。
1.3.2.2 修饰成员方法
格式:
public static void method(){
}
说明:
1.表示这个方法是一个静态方法,也称为类方法。
2.静态方法可以通过类名直接调用,而不需要实例化类对象。
3.静态方法不能访问非静态的成员变量和方法,只能访问静态变量和方法。
使用:
1.通过“类名.方法名()”的方式访问,例如:Animal.method()。
2.通过“对象名.方法名()”的方式访问,例如:Animal a = new Animal; a.method();。
通常我们都是使用方式一,以便区分静态方法和对象方法。
1.3.3 注意事项
- 静态不能直接访问非静态。原因:因为在内存当中是先有的静态方法,后有的非静态方法。
- 静态方法当中不能使用this关键字。原因:this代表当前对象,通过谁调用的方法,谁就是当前对象,但是静态方法的访问不用对象,所以就会产生矛盾。
1.3.4 静态代码块
格式:
public class 类名称{
static{
静态代码块
}
}
特点:
当第一次用到本类时,静态代码块执行的唯一的一次;静态内容总是优先于非静态,所以静态代码块比构造方法先执行。
静态代码块的典型用途:
用来一次性地对静态成员变量进行赋值。
题外话:
如果文章中存在错误的地方,欢迎大家指正出来,共同学习进步,后续会陆续就JAVA这门课程展开进行分享。