一.内部类
-
内部类是类中五大成分之一(成员变量,方法,构造器,代码块,内部类)
-
如果一个类定义在另一个类的内部,这个类就是内部类
-
场景:当一个类的内部,包含了一个完整的事物,且这个事物没有必要单独设计,就可以把这个事物设计成内部类
(1)成员内部类
就是定义一个类中成员位置的类,在内部类中也可以定义成员属性和方法
成员内部类中访问其他成员的特点:
1.成员内部中可以定义实例成员,静态成员(注意:静态成员从jdk16开始支持)
2.成员内部类中的实例方法,可以直接访问外部类的实例成员,静态成员
3.如果内部类和外部类出现了重名的成员,可以通过(外部类名.this.xxx)强行访问外部类的成员
public class Car {
//外部类的成员变量
private String name = "宝马";
//成员内部类
public class Engine {
private int power;
private String name = "发动机";
public static int price = 1000;
//3、如果内部和外部类出现了重名的成员,可以通过(外部类名.this.xxx) 强行访问外部类的成员
public void showName() {
System.out.println("name=" + Car.this.name);
}
public int getPower() {
return power;
}
public void setPower(int power) {
this.power = power;
}
}
}
(2)静态内部类
- 使用static修饰的内部类
- 静态内部类中访问外部类成员的特点:可以直接访问外部类的静态成员,不可以直接访问外部类的实例成员
- 定义类的格式:
public class Outer{
public static class Inner{
}
}
- 创建对象的格式
外部类名.内部类名 对象名 = new 外部类(..).内部类(..); Outer.Inner in = new Outer().Inner();
-
public class Demo { public static void main(String[] args) { //创建静态内部类对象 Outer.Inner inner = new Outer.Inner(); //静态内部类,只能方位外部类的静态成员 inner.show(); } } //创建外部类 class Outer { private static int age = 19; private String name = "张三"; //创建静态内部类 public static class Inner { public void show() { System.out.println(age); } } }
(3)局部内部类
是定义在方法中,代码中,构造器等执行中
(4)匿名内部类
- 作用 :更方便的创建一个子类对象(简化操作类、接口的代码)
- 格式 :
new 类名/接口名(参数值){
一般都是方法重写;
};
- 特点:本质是一个子类对象, 编译器会帮我们创建一个子类的对象
public class Demo { public static void main(String[] args) { //需求1: 创建Animal的子类对象,并调用方法 Animal a1 = new Dog(); a1.eat(); //需求2: 创建Bird的实现类对象,并调用方法 Bird b1 = new Crow(); b1.drink(); //需求3: 创建Animal的子类对象,并调用方法(匿名内部类实现) Animal a2 = new Animal() { @Override public void eat() { System.out.println("吃肉吃肉"); } }; a2.eat(); //需求4: 创建Bird的实现类对象,并调用方法(匿名内部类实现) Bird b2 = new Bird() { @Override public void drink() { System.out.println("喝水喝水"); } }; b2.drink(); } } //抽象类 abstract class Animal { public abstract void eat(); } //接口(Bird 鸟 Crow 乌鸦) interface Bird { void drink(); } class Crow implements Bird { @Override public void drink() { System.out.println("乌鸦喝水"); } }
(5)匿名内部类应用
- 匿名内部类应用场景------作为方法的参数进行传递 如果一个方法将一个抽象类/接口最为参数,那我们可以直接传递该抽象类/接口的匿名内部类对象
public class Demo { public static void main(String[] args) { //需求1: 调用test1方法,传入一个Animal的子类, 打印出 狗在吃肉 test1(new Animal() { @Override public void eat() { System.out.println("狗在吃肉"); } }); //需求2: 调用test2方法,传入一个bird的实现类, 打印出 乌鸦在喝水 test2(new Bird() { @Override public void drink() { System.out.println("乌鸦在喝水"); } }); } public static void test1(Animal animal) { animal.eat(); } public static void test2(Bird bird) { bird.drink(); } } //抽象类 abstract class Animal { public abstract void eat(); } //接口 interface Bird { void drink(); }
二.泛型
(1)认识泛型
- 定义: 定义类、接口、方法时,同时声明的类型变量(如:<E>) ,称为泛型。
- 作用 :泛型提供了在编译阶段约束所能操作的数据类型,并自动进行检查的能力! 这样可以避免强制类型转换,及其可能出现的异常。
- 本质:把具体的数据类型作为参数传给类型变量
- 分类:泛型类、泛型接口、泛型方法
-
public class Demo { public static void main(String[] args) { //需求1. 定义一个ArrayList不设置泛型, 保存元素, 然后遍历求元素的长度 // ArrayList list1 = new ArrayList(); // list1.add("关羽"); // list1.add("张飞"); // list1.add(1234); // list1.add(true); // for (int i = 0; i < list1.size(); i++) { // //多态 // Object obj = list1.get(i); // //强转 // String str = (String) obj; // //元素的长度 // System.out.println(str.length()); // } //需求2. 定义一个ArrayList, 设置泛型为String, 保存数据, 然后遍历求元素的长度 ArrayList<String> list2 = new ArrayList<String>(); list2.add("关羽"); list2.add("张飞"); list2.add("1234"); for (int i = 0; i < list2.size(); i++) { String str = list2.get(i); System.out.println(str.length()); } } }
(2)泛型类
- 在定义类的时候设置泛型, 然后在后续方法上使用
- 格式 修饰符 class 类名<类型变量,类型变量,…> {
}
- 注意: 类型变量建议用大写的英文字母,常用的有:E、T、K、V 等
public class Demo {
public static void main(String[] args) {
//1、创建MyList对象
MyList<String> list = new MyList<>();
//2、向MyList中存放数据
list.add("张飞");
System.out.println(list.get(0));
}
}
//自定义一个泛型类, 模仿ArrayList的add和get功能,只能存放10条数据
class MyList<E> {
//1、定义一个长度为10的数组
private Object [] arr = new Object[10];
//2、定一个当前存放元素的索引位置
private int index = 0;
//方法1:添加元素
public void add (E e) {
arr[index] = e;
//索引加+1
index ++ ;
}
//方法2:根据索引查询元素并返回
public E get(int index) {
return (E) arr[index];
}
}
(3)泛型接口
- 在定义接口的时候声明泛型
- 格式:修饰符 interface 类名<类型变量,类型变量,…> {
}
- 注意:类型变量建议用大写的英文字母,常用的有:E、T、K、V 等
-
public class Demo { public static void main(String[] args) { CommmonInterface ci = new TeacherInterfaceImpl(); ci.findByName("zhangsan"); } } //需求: 定义一个接口(接口中拥有两个功能: 保存对象数据和根据名称返回对象) //谁实现这个接口,谁就需要对两个功能做就提实现 interface CommmonInterface<E> { void add(E e); E findByName(String name); } //实现公共的泛型接口,并设置数据类型 class TeacherInterfaceImpl implements CommmonInterface<Teacher>{ @Override public void add(Teacher teacher) { } @Override public Teacher findByName(String name) { System.out.println("根据name查询老师:" + name); return null; } } class StudentInterfaceImpl implements CommmonInterface<Student>{ @Override public void add(Student student) { } @Override public Student findByName(String name) { return null; } }
(4)泛型方法
- 修饰符<类型变量1, 类型交最2> 返回值类型 方法名(参数列表){
}
- 注意: 类型变量建议使用大写的英文宁母,常见为E、T、K、V等
-
public class Demo { public static void main(String[] args) { //需求1: 调用test1方法,传入一个Animal的子类, 打印出 狗在吃肉 test1(new Animal() { @Override public void eat() { System.out.println("狗在吃肉"); } }); //需求2: 调用test2方法,传入一个bird的实现类, 打印出 乌鸦在喝水 test2(new Bird() { @Override public void drink() { System.out.println("乌鸦在喝水"); } }); } public static void test1(Animal animal) { animal.eat(); } public static void test2(Bird bird) { bird.drink(); } } //抽象类 abstract class Animal { public abstract void eat(); } //接口 interface Bird { void drink(); }
(5)泛型通配符
- 通配符 <?>,可以在使用泛型的时候,代表一切类型
- 泛型上限 <?extends 类名>,能接收指定类型利子类
- 泛型下限 <?super类名>,接收指定类型和父类
-
public class Demo { public static void main(String[] args) { //数据准备 ArrayList<Animal> animals = new ArrayList(); ArrayList<Dog> dogs = new ArrayList<Dog>(); ArrayList<Person> persons = new ArrayList<Person>(); ArrayList<Teacher> teachers = new ArrayList<Teacher>(); ArrayList<Student> students = new ArrayList<Student>(); //--------------需求1-------------------- m1(animals); m1(dogs); m1(persons); m1(teachers); m1(students); //---------------需求2------------------- //m2(animals); //m2(dogs); m2(persons); m2(teachers); m2(students); //---------------需求3------------------- m3(animals); //m3(dogs); m3(persons); //m3(teachers); //m3(students); } //需求1: 定义一个方法m1,参数为一个ArrayList集合,集合中可以存放任意类型的参数 public static void m1(ArrayList<?> list) { System.out.println(list.size()); } //需求2: 定义一个方法m2,参数为一个ArrayList集合,集合中可以存放Person及其子类型的参数 public static void m2(ArrayList< ? extends Person> list) { System.out.println(list.size()); } //需求3: 定义一个方法m3,参数为一个ArrayList集合,集合中可以存放Person及其父类型的参数 public static void m3(ArrayList< ? super Person> list) { System.out.println(list.size()); } } //动物 class Animal{ } //狗 class Dog extends Animal{ } //人 class Person extends Animal{ } //老师 class Teacher extends Person{ } //学生 class Student extends Person{ }
(6)注意事项
- 一旦将java文件编译为class文件,泛型就不存在了,称为泛型擦除
- 泛型不支持基本数据类型,只支持引用类型(存基本类型呢?后面学包装类)
public class Demo {
public static void main(String[] args) {
ArrayList<Integer> list = new ArrayList();
list.add(1);
//包装类,将基本数据类型处理,转化为引用类型
}
}
class MyList<E> {
private Object[] arr = new Object[10];
private int size;
//添加方法
public void add(E e) {
arr[size++] = e;
}
//获取方法
public E get(int index) {
return (E) arr[index];
}
}
三.api
(1)常量
- 命名规范: 单词全部大写,多个之间使用_连接
- 优点: 1. 代码可读性更好,可维护性也更好。 2. 程序编译后,出现常量的地方全部会被替换成其记住的字面量,这样可以保证使用常量和直接用字面量的性能是一样的。
public class Demo {
public static void main(String[] args) {
System.out.println("学校名称:" + Constant.SCHOOL_NAME);
//需求2: 保存学生的性别, 为提高性能,经常使用0和1表示性别
System.out.println(Constant.MALE);
}
}
//需求1: 定义一个常量,记录学校的名称
class Constant {
//记录所有的常量信息(状态,系统的配置)
public static final String SCHOOL_NAME="黑马程序员";
//性别
public static final int MALE = 0; //男
public static final int FEMALE = 1; //女
}
(2)枚举
- Java提供的一种用于简洁表示一些固定值的类
- 枚举类格式 :修饰符 enum 校举类名{ 校举顶1,校举项2..; 其他成员.. }
- 枚举的特点:
1、枚举类的第一行只能罗列一些名称,且默认都是常量,每个常量记住的就是枚举类的一个对象 2、枚举类的构造器都是私有的(自己提供也只能是私有的),因此枚举类对外不能创建对象
3、枚举都是最终类,不能被继承
4、枚举类中,从第二行开始,可以定义类的其他成员
5、编译器为枚举类新增了几个方法,并且所有校举类都是java.lang.Enum的子类,所以可以使用父类的方法
public class Demo {
public static void main(String[] args) {
//1. 需求: 创建Student对象,使用set赋值为:张三,MAN
Student student = new Student();
student.setName("张三");
student.setSex(Sex.FEMALE);
System.out.println(student);
}
}
//定义枚举类
enum Sex {
MALE,FEMALE;
}
class Student {
private String name;
private Sex sex; //使用枚举表示性别
public void setSex(Sex sex) {
this.sex = sex;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", sex='" + sex + '\'' +
'}';
}
}
(3)object
- Object类是Java中所有类的祖宗类,类中的方法可以被所有Java类所使用
- String toString(): 返回对象的字符串表示形式, 默认打印对象的内存地址, 一般用于子类重写返回对象的指定格式
- boolean equals(Object o): 判断两个对象是否相等, 默认比较两个对象的内存地址, 一般用于子类重写自定义比较规则
-
public class Demo { public static void main(String[] args) { //创建student1 Student student1 = new Student("张三",18); System.out.println(student1.toString()); System.out.println("-----------------"); //创建student2 Student student2 = new Student("张三",18); System.out.println(student2.toString()); System.out.println("-----------------"); //比较student1和student2 boolean equals = student1.equals(student2); System.out.println("两个对象是否相等:" + equals); //false } } class Student { String name; int age; public Student(String name, int age) { this.name = name; this.age = age; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Student student = (Student) o; return age == student.age && Objects.equals(name, student.name); } @Override public String toString() { return "Student{" + "name='" + name + '\'' + ", age=" + age + '}'; } }
Object类常用方法 protected object clone() 对象克隆
- 浅克隆: 将基本类型数值、引用类型的地址都拷贝一份
1、子类必须实现cloneable接口(标记接口),否则运行报CloneNotSupportedException
2、子类重写clone方法, 在里面直接调用父类提供的clone方法
- 深克降: 将基本类型数值、字符串的地址都拷贝一份; 其他引用类型的数据,会创建新对象完成拷贝(拷贝出新的地址)
1、子类必须实现cloneable接口(标记接口),否则运行报CloneNotSupportedException
2、子类重写clone方法, 在里面直接调用父类提供的clone方法
3、在clone方法中, 将克隆得到的对象中的引用类型重新手动clone一下再复制到对象中
public class Demo {
public static void main(String[] args) throws CloneNotSupportedException {
//1. 创建学生对象
Student student = new Student(1, "张三", "admin", new double[]{20, 30, 50});
System.out.println(student);
System.out.println(student.id);
System.out.println(student.username);
System.out.println(student.password);
System.out.println(student.scores);
//2. 克隆一个学生对象
System.out.println("=====================克隆对象=============================");
Student cloneStudent = (Student)student.clone();
System.out.println(cloneStudent);
System.out.println(cloneStudent.id);
System.out.println(cloneStudent.username);
System.out.println(cloneStudent.password);
System.out.println(cloneStudent.scores);
//修改原始数据
student.scores[0] = 99;
//打印克隆后对象
System.out.println(cloneStudent.scores[0]);
}
}
//CloneNotSupportedException : 不支持克隆
/**
* 1、java类实现空接口Cloneable
* 2、重写父类clone方法
*/
class Student implements Cloneable{
int id;
String username;
String password;
double[] scores;
public Student(int id, String username, String password, double[] scores) {
this.id = id;
this.username = username;
this.password = password;
this.scores = scores;
}
//重写父类的clone
@Override
protected Object clone() throws CloneNotSupportedException {
//1、调用父类的克隆方法,创建一个浅克隆对象
Student student = (Student) super.clone();
//2、对浅克隆对象中存在地址问题的引用类型数据,重新赋值
student.scores = student.scores.clone();
//3、返回
return student;
//return super.clone();
}
}
(4)objects
java提供的一个工具类
常见方法
- public static boolean equals(Object a, Object b); 先做非空判断,再比较两个参数是否相等 (更严谨安全,避免空指针)
- public static boolean isNull(Object o); 判断对象是否为null,是返回true
- public static boolean nonNull(Object o); 判断对象是否不为null,是返回true */
-
public class Demo { public static void main(String[] args) { //1. 定义两个字符串对象 String s1 = null; //NullPointerException 空指针异常:使用一个空对象中的方法或者属性 String s2 = "itheima"; //2. 判断两个对象是否相等 //boolean equals = s1.equals(s2); boolean equals = Objects.equals(s1, s2); System.out.println(equals); //3. 判断对象是否为空 boolean aNull = Objects.isNull(s1); System.out.println(aNull); //true //4. 判断对象是否不为空 boolean nonNull = Objects.nonNull(s1); System.out.println(nonNull); //true } }
(5)包装类
为了更好的支持面向对象, java为每一种基本类型都提供了一种对应的包装类型
具体
byte-->Byte short-->Short long-->Long float-->Float double-->Double boolean-->Boolean int-->Integer char-->Character
下面以Integer的角度学习, 其它都是类似的
- 创建对象
Integer(int value/String value) 构造方法(过时),接收int或string封装成Integer对象
static Integer valueOf(int i/String value) 替代构造方法,接收int或string封装成Integer对象
- 拆箱和装箱(基本类型和包装类的相互转换)
- 自动装箱: java支将基本类型直接值给对应包装类,底层使用的是valueOf()方法
- 自动拆箱: java支持将包装类直接赋值给对应基本类型,底层调intValue()方法
- 跟字符串的互相转换
static String toString(int i) 将Integer象封装的数值转为String类型
static int parseInt(String s) 将字符串数值转为int数值
static Integer Integer.valueOf(str) 将字符串转化成包装类
public class Demo {
public static void main(String[] args) {
//1. 创建对象
Integer in1 = new Integer(5);
System.out.println(in1);
Integer in2 = Integer.valueOf(10);
System.out.println(in2);
//2. 拆箱和装箱
Integer in3 = 8; //装箱:将基本类型的数据自动转化为包装类型
int tem = in3; //拆箱:将包装类型转化为基本类型数据
//3.跟字符串的互相转换
String string = in3.toString();
System.out.println(string);
String str = "100";
Integer in4 = Integer.valueOf(str);
System.out.println(in4);
int tem2 = Integer.parseInt(str);
System.out.println("tem2=" +tem2);
}
}
//面试题: 输出下面代码的执行原理
//Integer x = 100; //装箱
//x += 200; //先拆箱,再装箱 x = x + 200
//System.out.println(x);