java基础笔记-类与对象(特殊类)
内部类:
- 仅仅为外部类服务
- 可以隐藏该类的实现细节
- 可以方便的访问外部类的私有成员而不需要提供公有的get ,set方法
内部类分类:
普通内部类: 直接将类定义在另一个类的类体内
静态内部类: 使用static 修饰的内部类,属于类层级别
局部内部类:定义在方法体中的类
匿名内部类:没有名字的内部类(用的最多)
普通内部类:
又称成员内部类。
普通内部类的定义和使用例子:
package com.lagou.task10;
/**
* 编程实现普通内部类的定义和使用 - 文档注释
*/
public class NormalOuter {
private int cnt = 1;
// 定义普通内部类,隶属于外部类的成员,并且是对象层级
/*private*/public /*final*/ class NormalInner {
private int ia = 2;
private int cnt = 3;
public NormalInner() {
System.out.println("普通内部类的构造方法体执行到了!");
}
public void show() {
System.out.println("外部类中变量cnt的数值为:" + cnt); // 1
System.out.println("ia = " + ia); // 2
}
public void show2(int cnt) {
System.out.println("形参变量cnt = " + cnt); // 局部优先原则 4
System.out.println("内部类中cnt = " + this.cnt); // 3
System.out.println("外部类中cnt = " + NormalOuter.this.cnt); // 1
}
}
}
package com.lagou.task10;
public class NormalOuterTest {
public static void main(String[] args) {
// 1.声明NormalOuter类型的引用指向该类型的对象
NormalOuter no = new NormalOuter();
// 2.声明NormalOuter类中内部类的引用指向内部类的对象
NormalOuter.NormalInner ni = no.new NormalInner();
// 调用内部类中的show方法
ni.show();
System.out.println("---------------------------------------------");
ni.show2(4);
}
}
内部类 方法形参cnt ,内部类成员变量cnt,外部类成员变量cnt的区分:
内部类 方法形参cnt -> cnt
内部类成员变量cnt -> this.cnt
外部类成员变量cnt -> NormalOuter.this.cnt
静态内部类:
语法格式:
静态内部类定义例子:
package com.lagou.task10;
/**
* 实现静态内部类的定义和使用
*/
public class StaticOuter {
private int cnt = 1; // 隶属于对象层级
private static int snt = 2; // 隶属于类层级
public /*static*/ void show() {
System.out.println("外部类的show方法就是这里!");
}
/**
* 定义静态内部类 有static关键字修饰隶属于类层级
*/
public static class StaticInner {
private int ia = 3;
private static int snt = 4;
public StaticInner() {
System.out.println("静态内部类的构造方法哦!");
}
public void show() {
System.out.println("ia = " + ia); // 3
System.out.println("外部类中的snt = " + snt); // 2
//System.out.println("外部类的cnt = " + cnt); // Error:静态上下文中不能访问非静态的成员,因此此时可能还没有创建对象
}
public void show2(int snt) { // 就近原则
System.out.println("snt = " + snt); // 5
System.out.println("内部类中的成员snt = " + StaticInner.snt); // 4
System.out.println("外部类中的成员snt = " + StaticOuter.snt); // 2
//StaticOuter.show();
new StaticOuter().show();
}
}
}
package com.lagou.task10;
public class StaticOuterTest {
public static void main(String[] args) {
// 1.声明StaticInner类型的引用指向该类型的对象
StaticOuter.StaticInner si = new StaticOuter.StaticInner();
// 2.调用show方法进行测试
si.show();
System.out.println("---------------------------------------------");
si.show2(5);
}
}
静态内部类 方法形参snt ,静态内部类成员变量snt,静态外部类成员变量snt的区分:
静态内部类 方法形参snt -> snt
静态内部类成员变量snt ->StaticInner.snt
静态外部类成员变量snt -> StaticOuter.snt
静态 context 中 不能访问this,this 指当前类的对象
局部内部类:
局部内部类不能使用访问修饰符 和 static 关键字
局部内部类只在当前方法内部有效
局部内部类可以在方法体内部创建对象
局部内部类可以使用外部方法的局部变量,但是必须是理解为 final,这是因为局部内部类 和局部变量的声明周期不同所致
局部内部类使用的局部变量必须理解为final,虽然final 关键字可以省略,但是建议加上,提高可读性。
默认会拷贝一份局部变量到局部内部类使用,因此该局部变量不能改变。
局部内部类例子:
package com.lagou.task10;
/**
* 编程实现局部内部类的定义和使用
*/
public class AreaOuter {
private int cnt = 1;
public void show() {
// 定义一个局部变量进行测试,从Java8开始默认理解为final关键字修饰的变量
// 虽然可以省略final关键字,但建议还是加上
final int ic = 4;
// 定义局部内部类,只在当前方法体的内部好使 拷贝一份
class AreaInner {
private int ia = 2;
public AreaInner() {
System.out.println("局部内部类的构造方法!");
}
public void test() {
int ib = 3;
System.out.println("ia = " + ia); // 2
System.out.println("cnt = " + cnt); // 1
//ic = 5; Error
System.out.println("ic = " + ic); // 4
}
}
// 声明局部内部类的引用指向局部内部类的对象
AreaInner ai = new AreaInner();
ai.test();
}
}
package com.lagou.task10;
public class AreaOuterTest {
public static void main(String[] args) {
// 1.声明外部类类型的引用指向外部类的对象
AreaOuter ao = new AreaOuter();
// 2.通过show方法的调用实现局部内容类的定义和使用
ao.show();
}
}
回调模式:
回调模式是指——如果一个方法的参数是接口类型,则在调用该方法时, 需要创建并传递一个实现此接口类型的对象;
而该方法在运行时会调用 到参数对象中所实现的方法(接口中定义的)。
匿名内部类:
语法格式:• 接口/父类类型 引用变量名 = new 接口/父类类型() { 方法的重写 }
例子:
package com.lagou.task10;
public interface AnonymousInterface {
// 自定义抽象方法
public abstract void show();
}
package com.lagou.task10;
public class AnonymousInterfaceTest {
// 假设已有下面的方法,请问如何调用下面的方法?
// AnonymousInterface ai = new AnonymousInterfaceImpl();
// 接口类型的引用指向实现类型的对象,形成了多态
public static void test(AnonymousInterface ai) {
// 编译阶段调用父类版本,运行调用实现类重写的版本
ai.show();
}
public static void main(String[] args) {
//AnonymousInterfaceTest.test(new AnonymousInterface()); // Error:接口不能实例化
AnonymousInterfaceTest.test(new AnonymousInterfaceImpl());
System.out.println("---------------------------------------------------------------");
// 使用匿名内部类的语法格式来得到接口类型的引用,格式为:接口/父类类型 引用变量名 = new 接口/父类类型() { 方法的重写 };
AnonymousInterface ait = new AnonymousInterface() {
@Override
public void show() {
System.out.println("匿名内部类就是这么玩的,虽然你很抽象!");
}
};
// 从Java8开始提出新特性lamda表达式可以简化上述代码,格式为:(参数列表) -> {方法体}
AnonymousInterface ait2 = () -> System.out.println("lamda表达式原来是如此简单!");
AnonymousInterfaceTest.test(ait2);
}
}
lambda 表达式:
格式为:(参数列表) -> {方法体} //需要重写的方法参数列表 ,需要重写的方法体
枚举类:
package com.lagou.task10;
public interface DirectionInterface {
// 自定义抽象方法
public abstract void show();
}
package com.lagou.task10;
/**
* 编程实现所有方向的枚举,所有的方向:向上、向下、向左、向右 枚举类型要求所有枚举值必须放在枚举类型的最前面
*/
public enum DirectionEnum implements DirectionInterface {
// 2.声明本类类型的引用指向本类类型的对象
// 匿名内部类的语法格式:接口/父类类型 引用变量名 = new 接口/父类类型() { 方法的重写 };
// public static final Direction UP = new Direction("向上") { 方法的重写 };
UP("向上") {
@Override
public void show() {
System.out.println("贪吃蛇向上移动了一下!");
}
}, DOWN("向下") {
@Override
public void show() {
System.out.println("贪吃蛇向下移动了一下!");
}
}, LEFT("向左") {
@Override
public void show() {
System.out.println("左移了一下!");
}
}, RIGHT("向右") {
@Override
public void show() {
System.out.println("右移了一下!");
}
};
private final String desc; // 用于描述方向字符串的成员变量
// 通过构造方法实现成员变量的初始化,更加灵活
// 1.私有化构造方法,此时该构造方法只能在本类的内部使用
private DirectionEnum(String desc) { this.desc = desc; }
// 通过公有的get方法可以在本类的外部访问该类成员变量的数值
public String getDesc() {
return desc;
}
// 整个枚举类型只重写一次,所有对象调用同一个
/*@Override
public void show() {
System.out.println("现在可以实现接口中抽象方法的重写了!");
}*/
}
package com.lagou.task10;
/**
* 编程实现方向枚举类的测试,调用从Enum类中继承下来的方法
*/
public class DirectionEnumTest {
public static void main(String[] args) {
// 1.获取DirectionEnum类型中所有的枚举对象
DirectionEnum[] arr = DirectionEnum.values();
// 2.打印每个枚举对象在枚举类型中的名称和索引位置
for (int i = 0; i < arr.length; i++) {
System.out.println("获取到的枚举对象名称是:" + arr[i].toString());
System.out.println("获取到的枚举对象对应的索引位置是:" + arr[i].ordinal()); // 和数组一样下标从0开始
}
System.out.println("---------------------------------------------------------------");
// 3.根据参数指定的字符串得到枚举类型的对象,也就是将字符串转换为对象
//DirectionEnum de = DirectionEnum.valueOf("向下"); // 编译ok,运行发生IllegalArgumentException非法参数异常
DirectionEnum de = DirectionEnum.valueOf("DOWN");
//DirectionEnum de = DirectionEnum.valueOf("UP LEFT"); // 要求字符串名称必须在枚举对象中存在
//System.out.println("转换出来的枚举对象名称是:" + de.toString());
System.out.println("转换出来的枚举对象名称是:" + de); // 当打印引用变量时,会自动调用toString方法
System.out.println("---------------------------------------------------------------");
// 4.使用获取到的枚举对象与枚举类中已有的对象比较先后顺序
for(int i = 0; i < arr.length; i++) {
// 当调用对象在参数对象之后时,获取到的比较结果为 正数
// 当调用对象在参数对象相同位置时,则获取到的比较结果为 零
// 当调用对象在参数对象之前时,则获取到的比较结果为 负数
System.out.println("调用对象与数组中对象比较的先后顺序结果是:" + de.compareTo(arr[i]));
}
System.out.println("---------------------------------------------------------------");
// 5.使用数组中每个DirectionEnum对象都去调用show方法测试
for (int i = 0; i < arr.length; i++) {
arr[i].show();
}
}
}
注解:
Annotation ,引用数据类型,可以看作特殊接口
是一种标记,通过这些标记可以在编译、类加载、运行时做指定的处理。
注解继承 java.lang.annotation.Annotation 接口
没有成员的注解,叫标记或标识注解
注解成员变量: public String value();// 声明一个String类型的成员变量
注解只有成员变量,没有成员方法
注解的成员变量用无形参的方式来声明,方法名是成员变量的名字,返回值是成员变量的类型
注解只有一个成员的时候,一般使用value命名
注解的类型 可以是String ,8种基本数据类型,class,enum,Annotation类型
注解名称后,使用小括号 value = "hello"
注解成员变量给默认值: public String value() default “123”;
元注解:
可以对注解进行注解
@Retention 保持,生命周期,有效范围
@Documented 是否在文档注释中体现
@Target 表示注解可以修饰哪些内容
@Inherited 是否可继承
@Repeatable 可重复
@Retention :
取值:
RetentionPolicy.SOURCE //只在源码阶段保留,在编译时丢弃
RetentionPolicy.CLASS // 保留到class文件中,不被加载到JVM
RetentionPolicy.RUNTIME // 可以保留到运行时
使用示例:@Retention(RetentionPolicy.SOURCE)
@Documented:
javadoc 工具可以抽取类,方法,成员的注释,默认不包括注解内容
idea: Tools -》Generate JavaDoc 命令行参数指定编码 -encoding utf-8
@Documented 要求 @Retention(RetentionPolicy.RUNTIME)
用来表示注解能否被 javadoc工具提取
使用示例:
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Target:
指定被修饰的注解可以修饰哪些元素
取值:枚举类型
使用示例:
@Target({ElementType.TYPE, ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER})
@Inherited :
表示注解所修饰的类中的注解 可以被子类继承
package com.lagou.task10; import java.lang.annotation.*; //@Retention(RetentionPolicy.SOURCE) // 表示下面的注解在源代码中有效 //@Retention(RetentionPolicy.CLASS) // 表示下面的注解在字节码文件中有效,默认方式 @Retention(RetentionPolicy.RUNTIME) // 表示下面的注解在运行时有效 @Documented // 表示下面的注解信息可以被javadoc工具提取到API文档中,很少使用 // 表示下面的注解可以用于类型、构造方法、成员变量、成员方法、参数 的修饰 @Target({ElementType.TYPE, ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER}) @Inherited // 表示下面的注解所修饰的类中的注解使用可以被子类继承 // 若一个注解中没有任何的成员,则这样的注解叫做标记注解/标识注解 public @interface MyAnnotation { //public Direction value(); // 声明一个String类型的成员变量,名字为value 类型有要求 public String value() default "123"; // 声明一个String类型的成员变量,名字为value public String value2(); }
package com.lagou.task10; // 表示将标签MyAnnotation贴在Person类的代码中,使用注解时采用 成员参数名 = 成员参数值, ... //@MyAnnotation(value = "hello", value2 = "world") @MyAnnotation(value2 = "world") public class Person { /** * name是用于描述姓名的成员变量 */ @MyAnnotation(value2 = "1") private String name; /** * age是用于描述年龄的成员变量 */ private int age; /** * 编程实现无参构造方法 */ @MyAnnotation(value2 = "2") public Person() { } /** * 编程实现有参构造方法 * @param name * @param age */ public Person(@MyAnnotation(value2 = "4") String name, int age) { this.name = name; this.age = age; } /** * 自定义成员方法实现特征的获取和修改 * @return */ @MyAnnotation(value2 = "3") 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; } }
@Repeatable:
表示注解可以被重复使用
需要声明 成员变量是数组的注解
package com.lagou.task10; import java.lang.annotation.ElementType; import java.lang.annotation.Target; /** * 自定义注解里面可以描述多种角色 */ @Target(ElementType.TYPE_USE) public @interface ManTypes { ManType[] value(); }
package com.lagou.task10; import java.lang.annotation.ElementType; import java.lang.annotation.Repeatable; import java.lang.annotation.Target; /** * 自定义注解用于描述任务的角色 */ @Repeatable(value = ManTypes.class) @Target(ElementType.TYPE_USE) public @interface ManType { String value() default ""; }
package com.lagou.task10; @ManType(value = "职工") @ManType(value = "超人") //@ManTypes({@ManType(value = "职工"), @ManType(value = "超人")}) // 在Java8以前处理多个注解的方式 public class Man { @Deprecated // 表示该方法已经过时,不建议使用 public void show() { System.out.println("这个方法马上过时了!"); } public static void main(String[] args) { int ia = 97; char c1 = (@ManType char) ia; } }
预制注解:
@Override 重写
@Deprecated 过时
@SuppressWarings 抑制编译器警告
文章来自拉勾教育 大数据开发