1.JAVA泛型
Java 泛型(generics) 是 JDK 5 中引入的一个新特性, 泛型提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的类型。泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。
Java泛型可以保证如果程序在编译时没有发出警告,运行时就不会产生ClassCastException异常。
泛型的作用是:可以在类声明时通过一个标识表示类中某个属性的类型,或者是某个方法的返回值的类型,或者是参数类型。
class Person<E>{
//E就是这个标识
//E标识s的数据类型,该数据类型在定义Person对象的时候指定,即在编译期间就确定E是什么类型
E s;
public Person(E s){ // E也可以是参数类型
this.s = s;
}
public E f(){ //返回类型使用E
return s;
}
}
//如果我们在定义Person对象的时候,指定类型为String,那么上述代码块中的E则表示String,即所有的E都可以用String替代.
Person<String> person = new Person<String>("篮网总冠军2022!");
//如果我们在定义Person对象的时候,指定类型为Integer,那么上述代码块中的E则表示Integer,即所有的E都可以用Integer替代.
Person<Integer> person = new Person<Integer>(2022);
/*Person<Integer> person = new Person<Integer>("篮网总冠军2022!");
如果我们写成这样子就会立马报错(也就是在编译期间报错),而不用非得等到你运行程序的时候才会报错,效率就提高了。
*/
1.1泛型使用细节
1.给泛型指向数据类型要求是 “引用类型”,不能是 “基本数据类型”。
2.在给泛型指定具体类型后,可以传入改类型或者其子类类型
public class Main {
public static void main(String[] args) {
Pig<A> pig1 = new Pig<A>(new A());
Pig<A> pig2 = new Pig<A>(new B());//这里会报错 因为泛型指定了具体数据类型A 但是却传入了一个B的对象
}
}
class A{}
class B{}
class Pig<E> {
E e;
public Pig(E e){
this.e = e;
}
}
public class Main {
public static void main(String[] args) {
Pig<A> pig1 = new Pig<A>(new A());
Pig<A> pig2 = new Pig<A>(new B());
}
}
class A{}
class B extends A{}
class Pig<E> {
E e;
public Pig(E e){
this.e = e;
}
}
//将class B{}修改成class B extends A{},即让B称为A的子类就不会报错了。
//因为此时既可以传入指定类型A 也可以传入指定类型A的子类类型B
3.泛型的使用形式.
ArrayList<Integer> list1 = new ArrayList<>();
List<Integer> list2 = new ArrayList<>(); //可以用List接口来接收 “编译看左边、运行看右边”
在实际开发中,我们往往简写:
未简写:ArrayList<Integer> list1 = new ArrayList<Integer>();
简写:ArrayList<Integer> list1 = new ArrayList<>();
如果我们这样写 List list3 = new ArrayList();
默认给它的泛型是Object
1.2自定义泛型类
class Tiger<T, R, M> {
String name;
R r; //属性使用泛型
M m;
T t;
/*
static R r2;//这种写法报错
public static void m1(M m){ //这种写法报错
}
因为静态是和类相关的,在类加载时,对象还没有创建,所以如果静态方法和静态属性使用了泛型,JVM就无法完成初始化。
*/
//使用泛型的数组,不能初始化
//T[] ts = new T[8]; 这种写法就会报错,因为数组在 new 时不能确定 T 的类型,就无法在内存开空间
//但是如果只是定义 T[] ts; 则是可以的。
public Tiger(String name, R r, M m, T t) { //构造器使用泛型
this.name = name;
this.r = r;
this.m = m;
this.t = t;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public R getR() {
return r;
}
public void setR(R r) { //方法使用泛型
this.r = r;
}
public M getM() { //返回类型使用泛型
return m;
}
public void setM(M m) {
this.m = m;
}
public T getT() {
return t;
}
public void setT(T t) {
this.t = t;
}
}
1.3自定义泛型接口
1)接口中,静态成员也不能使用泛型(这个和泛型类规定一定)
2)泛型接口的类型,在继承接口或者实现接口时确定
3)没有指定类型,默认为Object
1.4自定义泛型方法
基本语法:
修饰符 <T,R…> 返回类型 方法名(参数列表){
}
1)泛型方法可以定义在普通类中,也可以定义在泛型类中。
class Car{ //普通类
public void run(){ //普通方法
}
public <T,R> void fly(T t,R r){ //泛型方法
}
}
class Fish<T,R>{ //泛型类
public void run(){ //普通方法
}
public <U,M> void eat(U u,M m){ //泛型方法
}
//泛型方法可以使用类声明的泛型,也可以使用自己声明的泛型
}
2)泛型方法被调用时,类型会确定。
Car car = new Car();
car.fly("越野",200);
//调用方法时,传入参数,编译器就会确定类型
3)“泛型方法“ 和 ”方法使用了泛型“ 的 区别
public void hi(T t){ //hi方法使用了类声明的泛型 T
}//注意区分泛型方法是怎么写的
1.5泛型的继承和通配符
1)泛型不具备继承性
List<Object> list = new ArrayList<String>();//这样写是不对的
//虽然String是Object的子类
2)
- <?>:支持任意泛型类型
- <? extends A>:支持A类以及A类的子类,规定了泛型的上限;
- <? super A>:支持A类以及A类的父类,不限于直接父类,规定了泛型的下限;
public class Main {
public static void main(String[] args) {
List<Object> list1 = new ArrayList<>();
List<String> list2 = new ArrayList<>();
List<AA> list3 = new ArrayList<>();
List<BB> list4 = new ArrayList<>();
List<CC> list5 = new ArrayList<>();
printCollection1(list1);//√
printCollection1(list2);//√
printCollection1(list3);//√
printCollection1(list4);//√
printCollection1(list5);//√
printCollection2(list1);//×
printCollection2(list2);//×
printCollection2(list3);//√
printCollection2(list4);//√
printCollection2(list5);//√
printCollection3(list1);//√
printCollection3(list2);//×
printCollection3(list3);//√
printCollection3(list4);//×
printCollection3(list5);//×
}
public static void printCollection1(List<?> c){
for (Object object : c){
System.out.println(object);
}
}
public static void printCollection2(List<? extends AA> c){
for (Object object : c){
System.out.println(object);
}
}
public static void printCollection3(List<? super AA> c){
for (Object object : c){
System.out.println(object);
}
}
}
class AA{
}
class BB extends AA {
}
class CC extends BB{
}
2.反射
通过外部文件配置,在不修改源码的情况下,来控制程序,也符合设计模式的开闭原则。
2.1反射机制
反射机制允许程序在执行期间借助于ReflectionAPI取得任何类的内部信息(比如成员变量、构造器、成员方法等),并能操作对象的属性及方法。反射在设计模式和框架底层都会用到。
加载完类之后,在堆中就产生了一个Class类型的对象(一个类只有一个Class对象),这个Class对象包含了类的完整结构信息,通过这个Class对象得到类的结构。这个对象就像一面镜子,透过这个镜子看到类的结构,所以形象的称之为:反射。
反射相关的主要类:
- java.lang.Class:代表一个类,Class对象表示某个类加载后在堆中的对象。
- java.lang.reflect.Method:代表类的方法,Method对象表示某个类的方法
- java.lang.reflect.Field:代表类的成员变量,Field对象表示某个类的成员变量
- java.lang.reflect.Constructor:代表类的构造方法,Constructor对象表示构造器
反射优点和缺点:
优点:可以动态的创建和使用对象(也是框架底层核心),使用灵活,没有反射机制,框架技术就失去底层支撑。
缺点:使用反射基本是解释执行,对执行速度有影响。
反射调用优化—关闭访问检查:
1.Method和Field、Constructor对象都有setAccessible()方法;
2.setAccessible作用是启动和禁用访问安全检查的开关
3.参数值为true表示反射的对象在使用时取消访问检查,提高反射的效率,参数值为false表示反射的对象执行访问检查。
2.2 Class类
Class类对象不是new出来的,而是系统创建的。
对于某个类的Class类对象,在内存中只有一份,因为类只加载一次 。
每个类的实例都会记得自己是由哪个Class实例所生成。
通过Class对象可以完整的得到一个类的完整结构,通过一系列API。
Class对象是存放在堆上的。
Class类常用方法:
Car.java文件
public class Car {
public String brand = "宝马";
public int price = 500;
public String color = "白色";
@Override
public String toString() {
return "Car{" +
"brand='" + brand + '\'' +
", price=" + price +
", color='" + color + '\'' +
'}';
}
}
public class Class01 {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchFieldException {
String classAllPath = "com.company.Car";
//1.获取到 Car类 对应的 Class对象
Class cls = Class.forName(classAllPath);
//2.输出 cls
System.out.println(cls);//显示cls对象是哪个类的Class对象
System.out.println(cls.getClass());//输出cls运行类型
//3.得到 包名
System.out.println(cls.getPackage().getName());
//4.得到 全类名
System.out.println(cls.getName());
//5.通过 cls 创建一个对象实例
Car car = (Car)cls.newInstance();
System.out.println(car);
//6.通过反射获取属性
Field brand = cls.getField("brand");
System.out.println(brand.get(car));
//7.通过反射给属性赋值
brand.set(car,"越野");
System.out.println(brand.get(car));
//8.得到所有的属性
System.out.println("==========所有的字段属性==========");
Field[] fields = cls.getFields();
for(Field f : fields){
System.out.println(f.getName());
}
}
}
/*
class com.company.Car
class java.lang.Class
com.company
com.company.Car
Car{brand='宝马', price=500, color='白色'}
宝马
越野
==========所有的字段属性==========
brand
price
color
*/
2.3 获取Class类对象
1.前提:
已知一个类的全类名,且该类在类路径下,可通过Class类的静态方法forName()获取,可能抛出ClassNotFoundException异常。
应用场景:多用于配置文件,读取类全路径,加载类。
String classAllPath = "com.company.Car";
//"com.company.Car" 是类的全类名,这里我直接写出来了,一般我们都是通过一些方法读取配置文件得到类的全路径
Class cls1 = Class.forName(classAllPath);
System.out.println(cls1);
//输出class com.company.Car
2.前提:
若已知具体的类,通过类的class获取,该方式最为安全可靠,程序性能最高。
应用场景:多用于参数传递,比如通过反射得到对应构造器对象。 ——————类名.class
Class cls2 = Car.class;
System.out.println(cls2);
//输出class com.company.Car
3.前提:
已知某个类的实例,调用该实例的getClass()方法获取Class对象。
应用场景:通过创建好的对象,获取Class对象。
Car car = new Car();
Class cls3 = car.getClass();
System.out.println(cls3);
//输出class com.company.Car
4.通过类加载器获取到类的Class对象。
//先得到类加载器
ClassLoader classLoader = car.getClass().getClassLoader();
//通过类加载器得到Class对象
Class cls4 = classLoader.loadClass(classAllPath);
System.out.println(cls3);
//输出class com.company.Car
//cls1、cls2、cls3、cls4其实是同一个对象
System.out.println(cls1.hashCode());
System.out.println(cls2.hashCode());
System.out.println(cls3.hashCode());
System.out.println(cls4.hashCode());
/*
hashcode都相同
460141958
460141958
460141958
460141958
*/
5.基本数据(int、char、boolean、float、double、byte、long、short)按如下方式得到Class类对象
Class<Integer> integerClass = int.class;
Class<Character> characterClass = char.class;
System.out.println(integerClass);
System.out.println(characterClass);
/*输出:
int
char
*/
6.基本数据类型对应的包装类,可以通过.TYPE得到Class类对象
Class<Integer> type1 = Integer.TYPE;
Class<Character> type2 = Character.TYPE;
System.out.println(type1);
System.out.println(type2);
/*输出:
int
char
*/
2.4 类加载
反射机制是java实现动态语言的关键,也就是通过反射实现类动态加载。
1.静态加载:编译时加载相关的类,如果没有则报错,依赖性太强。
2.动态记载:运行时加载需要的类,如果运行时不用该类,则不报错,降低了依赖性。
类加载五个阶段:
- 1.加载阶段:JVM在该阶段的主要目的是将字节码从不同的数据源(可能是class文件、也可能是jar包,甚至网络)转化为二进制字节流加载到内存中,并生成一个代表该类的java.lang.Class对象。
- 2.连接阶段–验证:目的是为了确保Class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。包括:文件格式验证(是否以魔数oxcafebabe开头)、元数据验证、字节码验证和符号引用验证。可以考虑使用 -Xverify:none 参数来关闭大部分的类验证措施,缩短虚拟机类加载的时间。
- 2.连接阶段–准备:JVM会在该阶段对静态变量,分配内存并默认初始化。这些变量所使用的内存都将在方法区中进行分配。
class A{
public int n1 = 10;
public static int n2 = 20;
public static final int n3 = 30;
}
//1. n1是实例属性,不是静态变量,因此在准备阶段,是不会分配内存的;
//2. n2是静态变量,分配内存n2 是默认初始化为0,而不是20
//3. n3是static final 是常量,它和静态变量不一样,因为一旦赋值就不变 n3 = 30
- 2.连接阶段–解析: 虚拟机将常量池内的符号引用替换为直接引用的过程。
- 3.Initialization(初始化):到初始化阶段,才真正开始执行类中定义的Java程序代码,此阶段是执行clinit()方法的过程。 clinit()方法是由编译器按语句在源文件中出现的顺序,依次自动收集类中的所有静态变量的赋值动作和静态代码块的语句,并进行合并。 虚拟机会保证一个类的clinit()方法在多线程环境中被正确的加锁、同步,如果多个线程同时去初始化一个类,那么只会有一个线程去执行这个类的clinit()方法,其他线程都需要去阻塞等待,直到活动线程clinit()方法完毕。
直接使用类的静态属性,也会导致类的加载。
2.5 通过反射获取类的结构信息 API多看就记住了 查查手册文档
第一组:java.lang.Class类
public class ReflectionUtils {
public static void main(String[] args){
}
@Test
public void api_01() throws ClassNotFoundException {
//得到Class对象
Class personCls = Class.forName("com.company.Person");
System.out.println("===============================");
//getName:获取全类名
System.out.println(personCls.getName());//com.company.Person
System.out.println("===============================");
//getSimpleName:获取简单类名
System.out.println(personCls.getSimpleName());//Person
System.out.println("===============================");
//getFields:获取所有public修饰的属性,包含本类以及父类的
Field[] fields = personCls.getFields();
for (Field field : fields) {
System.out.println(field.getName());
}
System.out.println("===============================");
//getDeclaredFields:获取本类中所有属性
Field[] declaredFields = personCls.getDeclaredFields();
for (Field declaredField: declaredFields) {
System.out.println("本类中所有属性:" + declaredField.getName());
}
System.out.println("===============================");
//getMethods:获取所有public修饰的方法,包含本类以及父类的
Method[] methods = personCls.getMethods();
for(Method method : methods){
System.out.println("本类以及本类所有父类的方法:" + method.getName());
}
System.out.println("===============================");
//getDeclaredMethods:获取本类中所有方法
Method[] declaredMethods = personCls.getDeclaredMethods();
for (Method declaredMethod : declaredMethods) {
System.out.println("本类所有方法:" + declaredMethod.getName());
}
System.out.println("===============================");
//getConstructors:获取本类所有public修饰的构造器,
Constructor[] constructors = personCls.getConstructors();
for (Constructor constructor : constructors) {
System.out.println("本类以及父类的构造器:" + constructor.getName());
}
System.out.println("===============================");
//getDeclaredConstructors:获取本类中所有构造器
Constructor[] declaredConstructors = personCls.getDeclaredConstructors();
for (Constructor declaredConstructor : declaredConstructors) {
System.out.println("本类所有的构造器:" + declaredConstructor.getName());
}
System.out.println("===============================");
//getPackage:以Package形式返回 包信息
System.out.println(personCls.getPackage());
System.out.println("===============================");
//getSuperClass:以Class形式返回父类信息
Class superclass = personCls.getSuperclass();
System.out.println("父类的class对象:" + superclass);
System.out.println("===============================");
//getInterfaces:以Class[]形式返回接口信息
Class[] interfaces = personCls.getInterfaces();
for (Class anInterface : interfaces) {
System.out.println("接口信息:" + anInterface);
}
System.out.println("===============================");
//getAnnotations:以Annotation[] 形式返回注解信息
Annotation[] annotations = personCls.getAnnotations();
for (Annotation annotation : annotations) {
System.out.println("注解信息:" + annotation);
}
System.out.println("===============================");
}
}
class A{
public String hobby;
public void hi(){
System.out.println("hi....");
}
public A(){}
}
interface IA{
}
interface IB{
}
@Deprecated
class Person extends A implements IA,IB{
//属性
public String name;
protected int age;
String job;
private double sal;
//构造器
public Person(){}
public Person(String name){}
private Person(String name,int age){}//私有的
//方法
public void m1(){
}
protected void m2(){
}
void m3(){
}
private void m4(){
}
/*
四种访问权限:
private:“私有”,被其修饰的属性和方法仅能被本类对象访问;
default:“缺省”默认的,被其修饰的属性和方法允许同一个包下访问;
protected:“受保护的”,被其修饰的属性和方法允许被不同包下的子类访问;
public:“公共”,被其修饰的属性和方法可以被任何对象访问;
*/
}
第二组:java.lang.reflect.Field类
- getModifiers:以int形式返回修饰符,【说明:默认修饰符是0,public是1,private是2,protected是4,static是8,final是16】
- getType:以Class形式返回类型。
- getName:返回属性名
第三组:java.lang.reflect.Method类
- getModifiers:以int形式返回修饰符,【说明:默认修饰符是0,public是1,private是2,protected是4,static是8,final是16】
- getReturnType: 以Class形式获取返回类型
- getName:返回方法名
- getParameterTypes:以Class[]返回参数类型数组
第四组:java.lang.reflect.Constructor类
- getModifiers:以int形式返回修饰符
- getName:返回构造器名(全类名)
- getParameterTypes:以Class[]返回参数类型数组
2.6 通过反射创建对象实例
package com.company;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class ReflectCreateInstance {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
//1.先获取User类的Class对象
Class<?> userClass = Class.forName("com.company.User");
//2.通过public的无参构造器创建实例
Object o = userClass.newInstance();
System.out.println(o);
//3.通过public的有参构造器创建实例
//先得到对应的构造器
Constructor<?> constructor = userClass.getConstructor(String.class);
//然后创建实例,并传入实参
Object KD_KI = constructor.newInstance("Champion");
System.out.println(KD_KI);
//4.通过非public的有参构造器创建实例
//先得到private的构造器对象
Constructor<?> declaredConstructor = userClass.getDeclaredConstructor(int.class, String.class);
//创建实例
declaredConstructor.setAccessible(true);//暴(力)破(解),使用反射可以访问private构造器
Object kyrie = declaredConstructor.newInstance(100, "凯里欧文");
System.out.println(kyrie);
}
}
class User{
private int age;
private String name;
public User(){
}
public User(String name){
this.name = name;
}
private User(int age, String name) {
this.age = age;
this.name = name;
}
@Override
public String toString() {
return "User{" +
"age=" + age +
", name='" + name + '\'' +
'}';
}
}
/*
User{age=0, name='null'}
User{age=0, name='Champion'}
User{age=100, name='凯里欧文'}
*/
2.7 通过反射访问类中的成员
1.反射操作属性
package com.company;
import java.lang.reflect.Field;
//演示反射操作属性
public class ReflectAccessProperty {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchFieldException {
//1.得到Student类对应的Class对象
Class<?> stuClass = Class.forName("com.company.Student");
//2.创建对象
Object o = stuClass.newInstance();
//3.使用反射得到age属性对象
Field age = stuClass.getField("age");
age.set(o,30);
System.out.println(o);
System.out.println(age.get(o));
//4.使用反射操作name 属性 name为私有静态属性
Field name = stuClass.getDeclaredField("name");
//对name进行爆破
name.setAccessible(true);
name.set(o,"欧文");
//name.set(null,"杜兰特");//因为name是static属性,因此 o 也可以写作null
System.out.println(o);
System.out.println(name.get(o));//获取属性值
//System.out.println(name.get(null));//这种写法一定要求name是static的
}
}
class Student{
public int age;
private static String name;
public Student(){
}
@Override
public String toString() {
return "Student [age=" + age + ", name=" + name + "]";
}
}
2.通过反射询问方法
package com.company;
//演示反射调用方法
public class ReflectAccessProperty {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchFieldException, NoSuchMethodException, InvocationTargetException {
//1.得到Boss类对应的Class对象
Class<?> bossClass = Class.forName("com.company.Boss");
//2.创建对象
Object o = bossClass.newInstance();
//3.调用public的hi方法
//3.1得到hi方法对象
Method hi = bossClass.getMethod("hi",String.class);
//3.2 调用
hi.invoke(o,"篮网总冠军!");
//System.out.println(o);
//4.调用private static方法
//4.1 得到say方法对象
Method say = bossClass.getDeclaredMethod("say", int.class, String.class, char.class);
//4.2因为 say方法是private,所以需要爆破
say.setAccessible(true);
System.out.println(say.invoke(say,33,"杜兰特",'男'));
//4.3 因为say方法是static的,还可以这样调用
System.out.println(say.invoke(null,33,"杜兰特",'男'));
//5.反射中如果方法有返回值,统一返回object 但是他运行类型和方法定义的返回类型是一致的
Object reVal = say.invoke(null,33,"杜兰特",'男' );
System.out.println("reVal的运行类型=" + reVal.getClass());
}
}
class Boss{
public int age;
private static String name;
public Boss(){
}
private static String say(int n, String s,char c){
return n + " " + s + " " + c;
}
public void hi(String s) {
System.out.println("hi " + s);
}
}
11.其他问题
11.1 Java实体类如果不重写toString方法,会如何?
例1:未重写toString方法
public class Main {
public static void main(String[] args) {
HashSet<Student> students = new HashSet<Student>();
students.add(new Student("jack",18));
students.add(new Student("Tom",133));
students.add(new Student("irving",11));
for(Student o : students){
System.out.println(o);
}
}
}
class Student{
private String name;
private int age;
public Student(String name, int age) {
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;
}
}
//输出结果为
/*
com.company.Student@74a14482
com.company.Student@4554617c
com.company.Student@1b6d3586
未重写时,该类默认继承Object,进行输出时调用的就是Object类里的toString方法;
该方法会返回一个字符串,该字符串由类名(对象是该类的一个实例)、标记符“@”和此对象哈希码的无符号十六进制表示组成。
虽然我们写的输出是:System.out.println(o);
而不是System.out.println(o.toString());
但是这两种写法得到的结果是一样的,因为第一种写法实际上 “隐式调用”了 toString() 方法。
*/
例2:重写toString方法
public class Main {
public static void main(String[] args) {
HashSet<Student> students = new HashSet<Student>();
students.add(new Student("jack",18));
students.add(new Student("Tom",133));
students.add(new Student("irving",11));
for(Student o : students){
System.out.println(o);
}
}
}
class Student{
private String name;
private int age;
public Student(String name, int age) {
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;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
//输出结果为
/*
Student{name='irving', age=11}
Student{name='Tom', age=133}
Student{name='jack', age=18}
*/
//输出的格式我们可以在toString方法里进行修改,这里我们默认使用idea的生成方法生成的格式。
nts = new HashSet();
students.add(new Student(“jack”,18));
students.add(new Student(“Tom”,133));
students.add(new Student(“irving”,11));
for(Student o : students){
System.out.println(o);
}
}
}
class Student{
private String name;
private int age;
public Student(String name, int age) {
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;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
//输出结果为
/*
Student{name=‘irving’, age=11}
Student{name=‘Tom’, age=133}
Student{name=‘jack’, age=18}
*/
//输出的格式我们可以在toString方法里进行修改,这里我们默认使用idea的生成方法生成的格式。