不可变集合
不可变集合是一个长度不可变,内容也无法修改的集合
使用场景
如果某个数据不能被修改,把它防御性地拷贝到不可变集合中是个很好的实践。
当集合对象被不可信的库调用时,不可变形式是安全的。
简单理解:
不想让别人修改集合中的内容
比如说:
1,斗地主的54张牌,是不能添加,不能删除,不能修改的
2,斗地主的打牌规则:单张,对子,三张,顺子等,也是不能修改的
3,用代码获取的操作系统硬件信息,也是不能被修改的
不可变集合分类
- 不可变的list集合
public class ListDemo {
public static void main(String[] args) {
List<String> list = List.of("张三", "李四", "王五", "赵六");
Iterator<String> it = list.iterator();
while (it.hasNext()){
String s = it.next();
System.out.println(s);
}
//list.remove("张三");//UnsupportedOperationException
//list.add("qx");//UnsupportedOperationException
list.set(0,"fzx");//UnsupportedOperationException
}
}
- 不可变的set集合
public class ImmutableDemo2 {
public static void main(String[] args) {
//细节:
//当我们要获取一个不可变的Set集合时,里面的参数一定要保证唯一性
//一旦创建完毕之后,是无法进行修改的,在下面的代码中,只能进行查询操作
//set集合不能有重复元素
Set<String> set = Set.of( "张三", "李四", "王五", "赵六");
for (String s : set) {
System.out.println(s);
}
System.out.println("-----------------------");
Iterator<String> it = set.iterator();
while(it.hasNext()){
String s = it.next();
System.out.println(s);
}
System.out.println("-----------------------");
//set.remove("王五");//UnsupportedOperationException
}
}
- 不可变的map集合
public class MapDemo {
public static void main(String[] args) {
/*
创建Map的不可变集合
细节1:
键是不能重复的
细节2:
Map里面的of方法,参数是有上限的,最多只能传递20个参数,10个键值对
细节3:
如果我们要传递多个键值对对象,数量大于10个,在Map接口中还有一个方法
*/
Map<String,String> map = Map.of("张三", "南京", "李四", "北京", "王五", "上海",
"赵六", "广州", "孙七", "深圳", "周八", "杭州",
"吴九", "宁波", "郑十", "苏州", "刘一", "无锡",
"陈二", "嘉兴");
Set<String> keys = map.keySet();
for (String key : keys) {
String value = map.get(key);
System.out.println(key+","+value);
}
map.put("fzx","qx");//UnsupportedOperationException
}
}
键值对个数大于10
public class ImmutableMapDemo {
public static void main(String[] args) {
/*
创建Map的不可变集合,键值对的数量超过10个
*/
//1.创建一个普通的Map集合
HashMap<String, String> hm = new HashMap<>();
hm.put("张三", "南京");
hm.put("李四", "北京");
hm.put("王五", "上海");
hm.put("赵六", "北京");
hm.put("孙七", "深圳");
hm.put("周八", "杭州");
hm.put("吴九", "宁波");
hm.put("郑十", "苏州");
hm.put("刘一", "无锡");
hm.put("陈二", "嘉兴");
hm.put("fzx", "深圳");
//获取到所有的键值对对象(Entry对象)
Set<Map.Entry<String, String>> entries = hm.entrySet();
//把entries变成一个数组
Map.Entry[] arr1 = new Map.Entry[0];
//toArray方法在底层会比较集合的长度跟数组的长度两者的大小
//如果集合的长度 > 数组的长度 :数据在数组中放不下,此时会根据实际数据的个数,重新创建数组
//如果集合的长度 <= 数组的长度:数据在数组中放的下,此时不会创建新的数组,而是直接用
Map.Entry[] arr2 = entries.toArray(arr1);
//不可变的map集合
Map map1 = Map.ofEntries(arr2);
//map1.put("qixuan","杭州");//UnsupportedOperationException
Map<Object, Object> map2 = Map.ofEntries(entries.toArray(new Map.Entry[0]));
//JDK10之后的方法,底层原理跟上面的一样
Map<String, String> map3 = Map.copyOf(hm);
map3.put("qixuan","杭州");//UnsupportedOperationException
}
}
Stream流
体验Stream流
-
案例需求
按照下面的要求完成集合的创建和遍历
- 创建一个集合,存储多个字符串元素
- 把集合中所有以"张"开头的元素存储到一个新的集合
- 把"张"开头的集合中的长度为3的元素存储到一个新的集合
- 遍历上一步得到的集合
-
原始方式示例代码
public static void main(String[] args) {
//集合的批量添加
ArrayList<String> list1 = new ArrayList<>(List.of("张sf","张wj","张cs","王hh","张l","谢gk"));
//遍历list1把以张开头的元素添加到list2中。
ArrayList<String> list2 = new ArrayList<>();
for (String s : list1) {
if(s.startsWith("张")){
list2.add(s);
}
}
ArrayList<String> list3 = new ArrayList<>();
//遍历list2集合,把其中长度为3的元素,再添加到list3中
for (String s : list2) {
if(s.length() == 3){
list3.add(s);
}
}
for (String s : list3) {
System.out.println(s);
}
}
}
- 使用Stream流示例代码
public class Test {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>(List.of("张sf","张wj","张cs","王hh","张l","谢gk"));
//Stream流
list.stream().filter(s->s.startsWith("张"))
.filter(s->s.length() == 3)
.forEach(s -> System.out.println(s));
}
}
- Stream流的好处
- 直接阅读代码的字面意思即可完美展示无关逻辑方式的语义:获取流、过滤姓张、过滤长度为3、逐一打印
- Stream流把真正的函数式编程风格引入到Java中
- 代码简洁
Stream流的常见生成方式
- Stream流的三类方法
- 获取Stream流
- 创建一条流水线,并把数据放到流水线上准备进行操作
- 中间方法
- 流水线上的操作
- 一次操作完毕之后,还可以继续进行其他操作
- 终结方法
- 一个Stream流只能有一个终结方法
- 是流水线上的最后一个操作
- 获取Stream流
- 生成Stream流的方式
-
Collection体系集合
使用默认方法stream()生成流, default Stream < E > stream()
-
Map体系集合
把Map转成Set集合,间接的生成流
-
数组
通过Arrays中的静态方法stream生成流
-
同种数据类型的多个数据
通过Stream接口的静态方法of(T… values)生成流
-
public class StreamDemo {
public static void main(String[] args) {
//Collection体系的集合可以使用默认方法stream()生成流
List<String> list = new ArrayList<>();
Stream<String> listStream = list.stream();
Set<String> set = new HashSet<>();
Stream<String> setStream = set.stream();
//Map体系的集合间接的生成流
Map<String,Integer> map = new HashMap<>();
Stream<String> keyStream = map.keySet().stream();
Stream<Integer> valueStream = map.values().stream();
Stream<Map.Entry<String, Integer>> entryStream = map.entrySet().stream();
//数组可以通过Arrays中的静态方法stream生成流
String[] strArray = {};
Stream<String> strStream = Arrays.stream(strArray);
//同种数据类型的多个数据可以通过Stream接口的静态方法of(T... values)生成流
Stream<String> stringStream = Stream.of("fzx", "qiqi", "zx");
Stream<Integer> integerStream = Stream.of(10, 20, 25);
}
}
Stream流中间操作方法
- 概念
中间操作的意思是,执行完此方法之后,Stream流依然可以继续执行其他操作 - 常见方法
Stream<T> filter(Predicate predicate) 用于对流中的数据进行过滤
Stream<T> limit(long maxSize) 返回此流中的元素组成的流,截取前指定参数个数的数据
Stream<T> skip(long n) 跳过指定参数个数的数据,返回由该流的剩余元素组成的流
static <T> Stream<T> concat(Stream a, Stream b) 合并a和b两个流为一个流
Stream<T> distinct() 返回由该流的不同元素(根据Object.equals(Object) )组成的流
- filter代码演示
public class StreamFifterDemo {
public static void main(String[] args) {
//Stream<T> filter(Predicate predicate):过滤
//Predicate接口中的方法 boolean test(T t):对给定的参数进行判断,返回一个布尔值
ArrayList<String> list = new ArrayList<>();
list.add("张sf");
list.add("张wj");
list.add("张cs");
list.add("王hh");
list.add("张l");
list.add("谢gk");
//filter方法获取流中的 每一个数据.
//而test方法中的s,就依次表示流中的每一个数据.
//我们只要在test方法中对s进行判断就可以了.
//如果判断的结果为true,则当前的数据留下.
//如果判断的结果为false,则当前数据就不要.
/*list.stream().filter(new Predicate<String>() {
@Override
public boolean test(String s) {
return s.startsWith("张");
}
}).forEach(s -> System.out.println(s));*/
//因为Predicate接口中只有一个抽象方法test
//所以我们可以使用lambda表达式来简化
list.stream().filter(s-> s.startsWith("张")).forEach(s -> System.out.println(s));
}
}
- limit&skip代码演示
public class StreamMain {
public static void main(String[] args) {
//创建一个集合,存储多个字符串元素
ArrayList<String> list = new ArrayList<String>();
list.add("aaa");
list.add("bbb");
list.add("ccc");
list.add("ddd");
list.add("eee");
list.add("fff");
//需求1:取前3个数据在控制台输出
list.stream().limit(4).forEach(s -> System.out.print(s+" "));
System.out.println();
//需求2:跳过3个元素,把剩下的元素在控制台输出
list.stream().skip(3).forEach(s -> System.out.print(s+" "));
System.out.println();
//需求3:跳过2个元素,把剩下的元素中前2个在控制台输出
list.stream().skip(2).limit(2).forEach(s -> System.out.print(s+" "));
}
}
- concat&distinct代码演示
public class StreamTest {
public static void main(String[] args) {
//创建一个集合,存储多个字符串元素
ArrayList<String> list = new ArrayList<String>();
list.add("aaa");
list.add("bbb");
list.add("ccc");
list.add("ddd");
list.add("eee");
list.add("fff");
//需求1:取前4个数据组成一个流
Stream<String> s1 = list.stream().limit(4);
//需求2:跳过2个数据组成一个流
Stream<String> s2 = list.stream().skip(2);
//需求3:合并需求1和需求2得到的流,并把结果在控制台输出
/*Stream.concat(s1,s2).forEach(s -> System.out.print(s+" "));
System.out.println();*/
//需求4:合并需求1和需求2得到的流,并把结果在控制台输出,要求字符串元素不能重复
Stream.concat(s1,s2).distinct().forEach(s -> System.out.print(s+" "));
}
}
Stream流终结操作方法
- 概念
- 终结操作的意思是:执行完此方法之后,Stream流将不能再执行其他操作
- 常见方法
void forEach(Consumer action) 对此流的每个元素执行操作
long count() 返回此流中的元素数
- 代码演示
public class EndStream {
public static void main(String[] args) {
//创建一个集合,存储多个字符串元素
ArrayList<String> list = new ArrayList<String>();
list.add("aaa");
list.add("bbb");
list.add("ccc");
list.add("ddd");
list.add("eee");
list.add("fff");
method(list);
//long count():返回此流中的元素数
long count = list.stream().count();
System.out.println(count);
}
public static void method(ArrayList<String> list){
// void forEach(Consumer action):对此流的每个元素执行操作
// Consumer接口中的方法void accept(T t):对给定的参数执行此操作
//在forEach方法的底层,会循环获取到流中的每一个数据.
//并循环调用accept方法,并把每一个数据传递给accept方法
//s就依次表示了流中的每一个数据.
list.stream().forEach(new Consumer<String>() {
@Override
public void accept(String s) {
System.out.print(s + " ");
}
});
System.out.println();
System.out.println("---------------------");
//lambda表达式的简化格式
//是因为Consumer接口中,只有一个accept方法,即是函数式接口
list.stream().forEach(s-> System.out.print(s+" "));
}
}
Stream流的收集操作
- 概念
对数据使用Stream流的方式操作完毕后,可以把流中的数据收集到集合中 - 常用方法
R collect(Collector collector) 把结果收集到集合中
- 工具类Collectors提供了具体的收集方式
public static <T> Collector toList() 把元素收集到List集合中
public static <T> Collector toSet() 把元素收集到Set集合中
public static Collector toMap(Function keyMapper,Function valueMapper) 把元素收集到Map集合中
- 代码示例
public class MyStream {
public static void main(String[] args) {
ArrayList<Integer> list1 = new ArrayList<>();
for (int i = 1; i <= 10; i++) {
list1.add(i);
}
list1.add(10);
list1.add(10);
list1.add(10);
list1.add(10);
list1.add(10);
//filter负责过滤数据的.
//collect负责收集数据.
//获取流中剩余的数据,但是他不负责创建容器,也不负责把数据添加到容器中.
//Collectors.toList() : 在底层会创建一个List集合.并把所有的数据添加到List集合中.
List<Integer> list = list1.stream().filter(number -> number % 2 == 0)
.collect(Collectors.toList());
System.out.println(list);
//set集合中元素不可重复
Set<Integer> set = list1.stream().filter(number -> number % 2 == 0)
.collect(Collectors.toSet());
System.out.println(set);
}
}
public class MyStreamDemo {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("fzx,20");
list.add("xuanxuan,25");
list.add("qiqi,23");
Map<String, String> map = list.stream().filter(new Predicate<String>() {
@Override
public boolean test(String s) {
String[] split = s.split(",");
int age = Integer.parseInt(split[1]);
return age >= 24;
}
}).collect(Collectors.toMap(
s -> s.split(",")[0],
s -> s.split(",")[1]
));
System.out.println(map);
}
}
Stream流综合练习
-
案例需求
现在有两个ArrayList集合,分别存储6名男演员名称和6名女演员名称,要求完成如下的操作- 男演员只要名字为3个字的前三人
- 女演员只要姓林的,并且不要第一个
- 把过滤后的男演员姓名和女演员姓名合并到一起
- 把上一步操作后的元素作为构造方法的参数创建演员对象,遍历数据
演员类Actor已经提供,里面有一个成员变量,一个带参构造方法,以及成员变量对应的get/set方法
代码实现
public class Main {
public static void main(String[] args) {
//创建集合
ArrayList<String> manList = new ArrayList<String>();
manList.add("周润发");
manList.add("成龙");
manList.add("刘德华");
manList.add("吴京");
manList.add("周星驰");
manList.add("李连杰");
ArrayList<String> womanList = new ArrayList<String>();
womanList.add("林心如");
womanList.add("张曼玉");
womanList.add("林青霞");
womanList.add("柳岩");
womanList.add("林志玲");
womanList.add("王祖贤");
//男演员只要名字为3个字的前三人
Stream<String> manStream = manList.stream().filter(s -> s.length() == 3).limit(3);
//女演员只要姓林的,并且不要第一个
Stream<String> womanStream = womanList.stream().filter(s -> s.startsWith("林")).skip(1);
//把过滤后的男演员姓名和女演员姓名合并到一起
Stream<String> concatStream = Stream.concat(manStream, womanStream);
// 将流中的数据封装成Actor对象之后打印
concatStream.forEach(s-> {
Actor actor = new Actor(s);
System.out.println(actor.getName());
});
}
}
class Actor {
private String name;
public Actor(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
方法引用
把已经有的方法拿过来用,当作函数式接口中抽象方法的方法体
- 引用处必须是函数式接口
- 被引用的方法必须已经存在
- 被引用方法的形参和返回值需要跟抽象方法一致
- 被引用方法的功能要满足当前需求
体验方法引用
-
方法引用的出现原因
- 在使用Lambda表达式的时候,我们实际上传递进去的代码就是一种解决方案:拿参数做操作
- 那么考虑一种情况:如果我们在Lambda中所指定的操作方案,已经有地方存在相同方案,那是否还有必要再写重复逻辑呢?答案肯定是没有必要
- 那我们又是如何使用已经存在的方案的呢?
- 这就是我们要讲解的方法引用,我们是通过方法引用来使用已经存在的方案
-
代码演示
public class Main {
public static void main(String[] args) {
Integer[] array = {2,4,5,6,1,3,7,10};
//匿名内部类
/*Arrays.sort(array, new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o2 - o1;
}
});*/
//lambda表达式
Arrays.sort(array,(o1, o2) -> o2 - o1);
//方法引用
//静态方法要用类名去调用
Arrays.sort(array,Main::subtraction);
for (int i = 0; i < array.length; i++) {
System.out.println(array[i]);
}
}
public static int subtraction(int num1,int num2){
return num2 - num1;
}
}
方法引用符
-
方法引用符
- :: 该符号为引用运算符,而它所在的表达式被称为方法引用
-
推导与省略
- 如果使用Lambda,那么根据“可推导就是可省略”的原则,无需指定参数类型,也无需指定的重载形式,它们都将被自动推导
- 如果使用方法引用,也是同样可以根据上下文进行推导
- 方法引用是Lambda的孪生兄弟
引用类方法
引用类方法,其实就是引用类的静态方法
-
格式
类名::静态方法
-
范例
Integer::parseInt
Integer类的方法:public static int parseInt(String s) 将此String转换为int类型数据
-
练习描述
- 定义一个接口(Converter),里面定义一个抽象方法 int convert(String s);
- 定义一个测试类(ConverterDemo),在测试类中提供两个方法
- 一个方法是:useConverter(Converter c)
- 一个方法是主方法,在主方法中调用useConverter方法
interface Converter{
public abstract int convert(String s);
}
public class ConverterDemo {
public static void main(String[] args) {
//lambda
useConvert( s -> Integer.parseInt(s));//1234
//方法引用
useConvert(Integer::parseInt);//1234
}
public static void useConvert(Converter c){
int num = c.convert("1234");
System.out.println(num);
}
}
-
使用说明
Lambda表达式被类方法替代的时候,它的形式参数全部传递给静态方法作为参数
引用对象的实例方法
引用对象的实例方法,其实就引用类中的成员方法
-
格式
对象::成员方法 -
范例
"String"::toUpperCase
String类中的方法:public String toUpperCase()
将此String所有字符转换为大写
-
练习描述
-
定义一个类(PrintString),里面定义一个方法
-
public void printUpper(String s):把字符串参数变成大写的数据,然后在控制台输出
-
定义一个接口(Printer),里面定义一个抽象方法
void printUpperCase(String s)
-
定义一个测试类(PrinterDemo),在测试类中提供两个方法
- 一个方法是:usePrinter(Printer p)
- 一个方法是主方法,在主方法中调用usePrinter方法
代码演示
-
class PrintString{
//把字符串参数变成大写的数据,然后在控制台输出
public void printUpper(String s){
String result = s.toUpperCase();
System.out.println(result);
}
}
interface Printer{
void printUpperCase(String s);
}
public class MethodCitationDemo {
public static void main(String[] args) {
/*usePrinter(new Printer() {
@Override
public void printUpperCase(String s) {
String result = s.toUpperCase();
System.out.println(result);
}
});*/
//方法引用
usePrinter(new PrintString() :: printUpper);
}
public static void usePrinter(Printer p){
//传递实参给接口中的抽象方法
p.printUpperCase("HelloWorld");
}
}
使用说明
Lambda表达式被对象的实例方法替代的时候,它的形式参数全部传递给该方法作为参数
引用类的实例方法
引用类的实例方法,其实就是引用类中的成员方法
-
格式
类名::成员方法
-
范例
String::substring
public String substring(int beginIndex,int endIndex)
从beginIndex开始到endIndex结束,截取字符串。返回一个子串,子串的长度为endIndex-beginIndex
-
练习描述
-
定义一个接口(MyString),里面定义一个抽象方法:
String mySubString(String s,int x,int y);
-
定义一个测试类(MyStringDemo),在测试类中提供两个方法
- 一个方法是:useMyString(MyString my)
- 一个方法是主方法,在主方法中调用useMyString方法
-
代码演示
interface MyString{
String mySubString(String s,int start,int end);
}
public class MyStringDemo {
public static void main(String[] args) {
useMyString((String s, int start, int end) ->
s.substring(start, end));
useMyString(String::substring);
}
public static void useMyString(MyString m){
//传递实参给接口中的抽象方法
String s = m.mySubString("HelloWorld", 0, 5);
System.out.println(s);
}
}
引用构造器(构造方法)
引用构造器,其实就是引用构造方法
(就是为了创建对象)
-
格式
类名::new
-
范例
Student::new
-
练习描述
-
定义一个类(Student),里面有两个成员变量(name,age)
并提供无参构造方法和带参构造方法,以及成员变量对应的get和set方法 -
定义一个接口(StudentBuilder),里面定义一个抽象方法
Student build(String name,int age); -
定义一个测试类(StudentDemo),在测试类中提供两个方法
- 一个方法是:useStudentBuilder(StudentBuilder s)
- 一个方法是主方法,在主方法中调用useStudentBuilder方法
-
-
代码演示
public class StudentDemo {
public static void main(String[] args) {
useStudentBuilder(new StudentBuilder() {
@Override
public Student build(String name, int age) {
return new Student(name,age);
}
});
useStudentBuilder(Student::new);
}
public static void useStudentBuilder(StudentBuilder sb){
Student s = sb.build("qixuan", 25);
System.out.println(s.getName()+","+s.getAge());
}
}
interface StudentBuilder{
Student build(String name,int age);
}
class Student{
private String name;
private int 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;
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public Student() {
}
}
引用数组的构造方法
为了创建一个数组
- 格式
数组名::new - 范例
int [ ] :: new - 代码演示
public class ArrayDemo {
public static void main(String[] args) {
//目的:创建一个指定类型的数组
//需求:集合中存储一些整数,收集到数组当中
ArrayList<Integer> list = new ArrayList<>();
Collections.addAll(list,1,2,3,4,5);
//收集到数组当中
Integer[] arr1 = list.stream().toArray(new IntFunction<Integer[]>() {
@Override
public Integer[] apply(int value) {
return new Integer[value];
}
});
Integer[] arr2 = list.stream().toArray(Integer[]::new);
System.out.println(Arrays.toString(arr1));//[1, 2, 3, 4, 5]
System.out.println(Arrays.toString(arr2));//[1, 2, 3, 4, 5]
}
}
方法引用综合练习
练习一:集合中存储一些字符串的数据,比如:fzx,20
收集到Student类型的数组中(使用方法引用)
public class Main {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list,"fzx,20","qq,21","xx,25");
//正常方法
Student[] students1 = list.stream().map(new Function<String, Student>() {
//可在Student类中写该方法来引用
@Override
public Student apply(String s) {
String[] str = s.split(",");
return new Student(str[0], Integer.parseInt(str[1]));
}
}).toArray(new IntFunction<Student[]>() {
@Override
public Student[] apply(int value) {
return new Student[value];
}
});
for (Student student : students1) {
System.out.println(student);
}
//方法引用
Student[] students2 = list.stream().map(Student::new).toArray(Student[]::new);
System.out.println(Arrays.toString(students2));
}
}
class Student{
private String name;
private int age;
public String getName() {
return name;
}
//重写方法用于引用
public Student(String s){
this.name = s.split(",")[0];
this.age = Integer.parseInt(s.split(",")[1]);
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
}
练习二:创建集合添加学生对象,对象属性:name,age
只获取名字并放到数组当中(使用方法引用)
public class MainPractice {
public static void main(String[] args) {
ArrayList<Student> list = new ArrayList<>();
Student s1 = new Student("fzx",20);
Student s2 = new Student("zx",22);
Student s3 = new Student("fx",23);
Student s4 = new Student("fz",21);
Collections.addAll(list, s1,s2,s3,s4);
String[] str1 = list.stream().map(new Function<Student, String>() {
@Override
public String apply(Student student) {
return student.getName();
}
}).toArray(String[]::new);
System.out.println(Arrays.toString(str1));
//方法引用
String[] str2 = list.stream().map(Student::getName).toArray(String[]::new);
System.out.println(Arrays.toString(str2));
}
}
class Student{
private String name;
private int 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;
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public Student() {
}
}