Java:内部类、枚举、泛型以及常用API --黑马笔记

内部类

内部类是类中的五大成分之一(成员变量、方法、构造器、内部类、代码块),如果一个类定义在另一个类的内部,这个类就是内部类。

当一个类的内部,包含一个完整的事物,且这个事物没有必要单独设计时,就可以把这个事物设计成内部类。

比如:汽车的内部有发动机,发动机是包含在汽车内部的一个完整事物,可以把发动机设计成内部类。

public class Car{//内部类public class Engine{}
}

内部类有四种形式,分别是成员内部类、静态内部类、局部内部类、匿名内部类 。

1.1 成员内部类

 成员内部类就是类中的一个普通成员,类似于成员变量、成员方法。

public class Outer {private int age = 99;public static String a="黑马";// 成员内部类public class Inner{private String name;private  int age = 88;//在内部类中既可以访问自己类的成员,也可以访问外部类的成员public void test(){System.out.println(age); //88System.out.println(a);   //黑马int age = 77;System.out.println(age); //77System.out.println(this.age); //88System.out.println(Outer.this.age); //99}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;}}
}

成员内部类如何创建对象,格式如下:

//外部类.内部类 变量名 = new 外部类().new 内部类();
Outer.Inner in = new Outer().new Inner();
//调用内部类的方法
in.test();

内部类访问成员的特点:

1.既可以访问内部类成员、也可以访问外部类成员

2.如果内部类成员和外部类成员同名,可以使用  类名.this.成员   区分

1.2 静态内部类

静态内部类,其实就是在成员内部类的前面加了一个static关键字。静态内部类属于外部类自己持有。

public class Outer {private int age = 99;public static String schoolName="黑马";// 静态内部类public static class Inner{//静态内部类访问外部类的静态变量,是可以的;//静态内部类访问外部类的实例变量,是不行的public void test(){System.out.println(schoolName); //99//System.out.println(age);   //报错}}
}

静态内部类创建对象时,需要使用外部类的类名调用。

//格式:外部类.内部类 变量名 = new 外部类.内部类();
Outer.Inner in = new Outer.Inner();
in.test();

1.3 局部内部类

局部内部类是定义在方法中的类,和局部变量一样,只能在方法中有效。所以局部内部类的局限性很强,一般在开发中是不会使用的。

public class Outer{public void test(){//局部内部类class Inner{public void show(){System.out.println("Inner...show");}}//局部内部类只能在方法中创建对象,并使用Inner in = new Inner();in.show();}
}

1.4 匿名内部类

相比于前面几种内部类,匿名内部类就比较重要的。

我们还是先认识一下什么是匿名内部类?

匿名内部类是一种特殊的局部内部类;所谓匿名,指的是程序员不需要为这个类声明名字。

下面就是匿名内部类的格式:

new 父类/接口(参数值){@Override重写父类/接口的方法;
}

匿名内部类本质上是一个没有名字的子类对象、或者接口的实现类对象。

比如,先定义一个Animal抽象类,里面定义一个cry()方法,表示所有的动物有叫的行为,但是因为动物还不具体,cry()这个行为并不能具体化,所以写成抽象方法。

public abstract class Animal{public abstract void cry();
}

接下来,我想要在不定义子类的情况下创建Animal的子类对象,就可以使用匿名内部类

public class Test{public static void main(String[] args){//这里后面new 的部分,其实就是一个Animal的子类对象//这里隐含的有多态的特性: Animal a = Animal子类对象;Animal a = new Animal(){@Overridepublic void cry(){System.out.println("猫喵喵喵的叫~~~");}}a.eat(); //直线上面重写的cry()方法}
}

需要注意的是,匿名内部类在编写代码时没有名字,编译后系统会为自动为匿名内部类生产字节码,字节码的名称会以外部类$1.class的方法命名

匿名内部类的作用:简化了创建子类对象、实现类对象的书写格式。

我们再来看一下匿名内部类在实际中的应用场景。

只有在调用方法时,当方法的形参是一个接口或者抽象类,为了简化代码书写,而直接传递匿名内部类对象给方法。这样就可以少写一个类。比如,看下面代码:

public interface Swimming{public void swim();
}
public class Test{public static void main(String[] args){Swimming s1 = new Swimming(){public void swim(){System.out.println("狗刨飞快");}};go(s1);Swimming s1 = new Swimming(){public void swim(){System.out.println("猴子游泳也还行");}};go(s1);}//形参是Swimming接口,实参可以接收任意Swimming接口的实现类对象public static void go(Swimming s){System.out.println("开始~~~~~~~~");s.swim();System.out.println("结束~~~~~~~~");}
}

二、枚举

2.1 认识枚举

枚举是我们以后在项目开发中偶尔会用到的知识。

枚举是一种特殊的类,它的格式是:

public enum 枚举类名{枚举项1,枚举项2,枚举项3;
}

其实枚举项就表示枚举类的对象,只是这些对象在定义枚举类时就预先写好了,以后就只能用这几个固定的对象。

既然枚举是一个类的话,我们能不能在枚举类中定义构造器、成员变量、成员方法呢?答案是可以的。来看一下代码吧。

public enum A{//定义枚举项X,Y,Z("张三"); //枚举项后面加括号,就是在执行枚举类的带参数构造方法。//定义空构造器public A(){}//成员变量private String name;//定义带参数构造器public A(String name){this.name=name;}//成员方法public String getName(){return name;}...
}

虽然枚举类中可以像类一样,写一些类的其他成员,但是一般不会这么写,如果你真要这么干的话,到不如直接写普通类来的直接。

2.2 枚举的应用场景

枚举的应用场景是这样的:枚举一般表示一组信息,然后作为参数进行传输。

我们来看一个案例。比如我们现在有这么一个应用,用户进入应用时,需要让用户选择是女生、还是男生,然后系统会根据用户选择的是男生,还是女生推荐不同的信息给用户观看。

这里我们就可以先定义一个枚举类,用来表示男生、或者女生:

public class Constant{BOY,GRIL
}

再定义一个测试类,完成用户进入系统后的选择:

public class Test{public static void main(String[] args){//调用方法,传递男生provideInfo(Constant.BOY);}public static void provideInfo(Constant c){switch(c){case BOY:System.out.println("展示一些信息给男生看");break;case GRIL:System.out.println("展示一些信息给女生看");break;}}
}

最终再总结一下枚举的应用场景:枚举一般表示几个固定的值,然后作为参数进行传输

三、泛型

3.1 认识泛型

所谓泛型指的是,在定义类、接口、方法时,同时声明了一个或者多个类型变量(如:<E>),称为泛型类、泛型接口、泛型方法、它们统称为泛型。比如我们前面学过的ArrayList类就是一个泛型类。

ArrayList集合的设计者在定义ArrayList集合时,就已经明确ArrayList集合时给别人装数据用的,但是别人用ArrayList集合时候,装什么类型的数据他不知道,所以就用一个<E>表示元素的数据类型。 当别人使用ArrayList集合创建对象时,new ArrayList<String>就表示元素为String类型,new ArrayList<Integer>表示元素为Integer类型。

我们总结一下泛型的作用、本质:

1.泛型的好处:在编译阶段可以避免出现一些非法的数据。

2.泛型的本质:把具体的数据类型传递给类型变量。

3.2 自定义泛型类

泛型类,在实际工作中一般都是源代码中写好,我们直接用的,就是ArrayList<E>这样的,自己定义泛型类是非常少的。

自定义泛型类的格式如下:

//这里的<T,W>其实指的就是类型变量,可以是一个,也可以是多个。
public class 类名<T,W>{}

接下来,我们自己定义一个MyArrayList<E>泛型类,模拟一下自定义泛型类的使用。注意这里重点仅仅只是模拟泛型类的使用,所以方法中的一些逻辑是次要的,也不会写得太严谨。

//定义一个泛型类,用来表示一个容器
//容器中存储的数据,它的类型用<E>先代替用着,等调用者来确认<E>的具体类型。
public class MyArrayList<E>{private Object[] array = new Object[10];//定一个索引,方便对数组进行操作private int index;//添加元素public void add(E e){array[index]=e;index++;}//获取元素public E get(int index){return (E)array[index];}
}

接下来,我们写一个测试类,来测试自定义的泛型类MyArrayList是否能够正常使用

public class Test{public static void main(String[] args){//1.确定MyArrayList集合中,元素类型为String类型MyArrayList<String> list = new MyArrayList<>();//此时添加元素时,只能添加String类型list.add("张三");list.add("李四");//2.确定MyArrayList集合中,元素类型为Integer类型MyArrayList<Integer> list1 = new MyArrayList<>();//此时添加元素时,只能添加String类型list.add(100);list.add(200);}
}

3.3 自定义泛型接口

泛型接口其实指的是在接口中把不确定的数据类型用<类型变量>表示。定义格式如下:

//这里的类型变量,一般是一个字母,比如<E>
public interface 接口名<类型变量>{}

比如,我们现在要做一个系统要处理学生和老师的数据,需要提供2个功能,保存对象数据、根据名称查询数据,要求:这两个功能处理的数据既能是老师对象,也能是学生对象。

首先我们得有一个学生类和老师类:

public class Teacher{}
public class Student{}

我们定义一个Data<T>泛型接口,T表示接口中要处理数据的类型。

public interface Data<T>{public void add(T t);public ArrayList<T> getByName(String name);
}

接下来,我们写一个处理Teacher对象的接口实现类:

//此时确定Data<E>中的E为Teacher类型,
//接口中add和getByName方法上的T也都会变成Teacher类型
public class TeacherData implements Data<Teacher>{public void add(Teacher t){}public ArrayList<Teacher> getByName(String name){}
}

接下来,我们写一个处理Student对象的接口实现类:

//此时确定Data<E>中的E为Student类型,
//接口中add和getByName方法上的T也都会变成Student类型
public class StudentData implements Data<Student>{public void add(Student t){}public ArrayList<Student> getByName(String name){}
}

在实际工作中,一般也都是框架底层源代码把泛型接口写好,我们实现泛型接口就可以了。

3.4 泛型方法

泛型方法的格式:

public <泛型变量,泛型变量> 返回值类型 方法名(形参列表){}

接下我们看一个泛型方法的案例:

public class Test{public static void main(String[] args){//调用test方法,传递字符串数据,那么test方法的泛型就是String类型String rs = test("test");//调用test方法,传递Dog对象,那么test方法的泛型就是Dog类型Dog d = test(new Dog()); }//这是一个泛型方法<T>表示一个不确定的数据类型,由调用者确定public static <T> test(T t){return t;}
}

3.5 泛型限定

泛型限定的意思是对泛型的数据类型进行范围的限制。有如下的三种格式:

1.<?> 表示任意类型

2.<? extends 数据类型> 表示指定类型或者指定类型的子类

3.<? super 数据类型> 表示指定类型或者指定类型的父类

下面我们演示一下,假设有Car作为父类,BENZ,BWM两个类作为Car的子类,代码如下:

class Car{}
class BENZ extends Car{}
class BWN extends Car{}public class Test{public static void main(String[] args){//1.集合中的元素不管是什么类型,test1方法都能接收ArrayList<BWM> list1 = new ArrayList<>();ArrayList<Benz> list2 = new ArrayList<>();ArrayList<String> list3 = new ArrayList<>();test1(list1);test1(list2);test1(list3);//2.集合中的元素只能是Car或者Car的子类类型,才能被test2方法接收ArrayList<Car> list4 = new ArrayList<>();ArrayList<BWM> list5 = new ArrayList<>();test2(list4);test2(list5);//2.集合中的元素只能是Car或者Car的父类类型,才能被test3方法接收ArrayList<Car> list6 = new ArrayList<>();ArrayList<Object> list7 = new ArrayList<>();test3(list6);test3(list7);}public static void test1(ArrayList<?> list){}public static void test2(ArrayList<? extends Car> list){}public static void test3(ArrayList<? super Car> list){}
}

3.6 泛型擦除

什么是泛型擦除呢?也就是说泛型只能编译阶段有效,一旦编译成字节码,字节码中是不包含泛型的。而且泛型只支持引用数据类型,不支持基本数据类型。

四、常用API

API(Application Programming interface)意思是应用程序编程接口,说人话就是Java帮我们写好的一些程序,如:类、方法等,我们直接拿过来用就可以解决一些问题。

4.1 Object类

我们先来学习toString()方法。

public String toString()调用toString()方法可以返回对象的字符串表示形式。默认的格式是:“包名.类名@哈希值16进制”

假设有一个学生类如下:

public class Student{private String name;private int age;public Student(String name, int age){this.name=name;this.age=age;}
}

再定义一个测试类:

public class Test{public static void main(String[] args){Student s1 = new Student("赵敏",23);System.out.println(s1.toString()); }
}

打印结果如下:

如果,在Student类重写toString()方法,那么我们可以返回对象的属性值,代码如下:

public class Student{private String name;private int age;public Student(String name, int age){this.name=name;this.age=age;}@Overridepublic String toString(){return "Student{name=‘"+name+"’, age="+age+"}";}
}

运行测试类,结果如下:

接下来,我们学习一下Object类的equals方法:

public boolean equals(Object o)判断此对象与参数对象是否"相等"

我们写一个测试类,测试一下:

public class Test{public static void main(String[] args){Student s1 = new Student("赵薇",23);Student s2 = new Student("赵薇",23);//equals本身也是比较对象的地址,和"=="没有区别System.out.println(s1.equals(s2)); //false//"=="比较对象的地址System.out.println(s1==s2); //false}
}

但是如果我们在Student类中,把equals方法重写了,就按照对象的属性值进行比较:

public class Student{private String name;private int age;public Student(String name, int age){this.name=name;this.age=age;}@Overridepublic String toString(){return "Student{name=‘"+name+"’, age="+age+"}";}//重写equals方法,按照对象的属性值进行比较@Overridepublic boolean equals(Object o) {if (this == o) return true;if (o == null || getClass() != o.getClass()) return false;Student student = (Student) o;if (age != student.age) return false;return name != null ? name.equals(student.name) : student.name == null;}
}

再运行测试类,效果如下:

接下来,我们学习Object类的clone()方法,克隆。意思就是某一个对象调用这个方法,这个方法会复制一个一模一样的新对象,并返回。

clone方法是Object类中的一个被protected和native修饰的方法,被native就代表它的实现源码是用c++实现的,只不过是我们无法去修改它的代码罢了。

public Object clone()克隆当前对象,返回一个新对象

想要调用clone()方法,必须让被克隆的类实现Cloneable接口。如我们准备克隆User类的对象,代码如下:

public class User implements Cloneable{private String id; //编号private String username; //用户名private String password; //密码private double[] scores; //分数public User() {}public User(String id, String username, String password, double[] scores) {this.id = id;this.username = username;this.password = password;this.scores = scores;}//...get和set...方法自己加上@Overrideprotected Object clone() throws CloneNotSupportedException {return super.clone();}
}

接着,我们写一个测试类,克隆User类的对象。

public class Test {public static void main(String[] args) throws CloneNotSupportedException {User u1 = new User(1,"zhangsan","wo666",new double[]{99.0,99.5});//调用方法克隆得到一个新对象User u2 = (User) u1.clone();System.out.println(u2.getId());System.out.println(u2.getUsername());System.out.println(u2.getPassword());System.out.println(u2.getScores()); }
}

我们发现,克隆得到的对象u2它的属性值和原来u1对象的属性值是一样的。

上面演示的克隆方式,是一种浅克隆的方法,浅克隆的意思:拷贝出来的对象封装的数据与原对象封装的数据一模一样(引用类型拷贝的是地址值)。如下图所示:

还有一种拷贝方式,称之为深拷贝,拷贝原理如下图所示:

下面演示一下深拷贝User对象:

public class User implements Cloneable{private String id; //编号private String username; //用户名private String password; //密码private double[] scores; //分数public User() {}public User(String id, String username, String password, double[] scores) {this.id = id;this.username = username;this.password = password;this.scores = scores;}//...get和set...方法自己加上@Overrideprotected Object clone() throws CloneNotSupportedException {//先克隆得到一个新对象User u = (User) super.clone();//再将新对象中的引用类型数据,再次克隆u.scores = u.scores.clone();return u;}
}

4.2 Objects类

Objects是一个工具类,提供了一些方法可以对任意对象进行操作。主要方法如下:

下面写代码演示一下这几个方法:

public class Test{public static void main(String[] args){String s1 = null;String s2 = "itheima";//这里会出现NullPointerException异常,调用者不能为nullSystem.out.println(s1.equals(s2));//此时不会有NullPointerException异常,底层会自动先判断空System.out.println(Objects.equals(s1,s2));//判断对象是否为null,等价于==System.out.println(Objects.isNull(s1)); //trueSystem.out.println(s1==null); //true//判断对象是否不为null,等价于!=System.out.println(Objects.nonNull(s2)); //trueSystem.out.println(s2!=null); //true}
}

4.3 基本类型包装类

为什么要学习包装类呢?因为在Java中有一句很经典的话,万物皆对象。Java中的8种基本数据类型还不是对象,所以要把它们变成对象,变成对象之后,可以提供一些方法对数据进行操作。

Java中8种基本数据类型都用一个包装类与之对一个,如下图所示:

我们学习包装类,主要学习两点:

1.创建包装类的对象方式、自动装箱和拆箱的特性;

2. 利用包装类提供的方法对字符串和基本类型数据进行相互转换

我们先来学习,创建包装类对象的方法,以及包装类的一个特性叫自动装箱和自动拆箱。我们以Integer为例,其他的可以自己学,都是类似的。

//1.创建Integer对象,封装基本类型数据10
Integer a = new Integer(10);//2.使用Integer类的静态方法valueOf(数据)
Integer b = Integer.valueOf(10);//3.还有一种自动装箱的写法(意思就是自动将基本类型转换为引用类型)
Integer c = 10;//4.有装箱肯定还有拆箱(意思就是自动将引用类型转换为基本类型)
int d = c;//5.装箱和拆箱在使用集合时就有体现
ArrayList<Integer> list = new ArrayList<>();
//添加的元素是基本类型,实际上会自动装箱为Integer类型
list.add(100);
//获取元素时,会将Integer类型自动拆箱为int类型
int e = list.get(0);

在开发中,经常使用包装类对字符串和基本类型数据进行相互转换。

1.把字符串转换为数值型数据:包装类.parseXxx(字符串)

public static int parseInt(String s)把字符串转换为基本数据类型

2.将数值型数据转换为字符串:包装类.valueOf(数据):

public static String valueOf(int a)把基本类型数据转换为

写一个测试类演示一下:

//1.字符串转换为数值型数据
String ageStr = "29";
int age1 = Integer.parseInt(ageStr);String scoreStr = 3.14;
double score = Double.prarseDouble(scoreStr);//2.整数转换为字符串,以下几种方式都可以(挑中你喜欢的记一下)
Integer a = 23;
String s1 = Integer.toString(a);
String s2 = a.toString();
String s3 = a+"";
String s4 = String.valueOf(a);

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/461601.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

(1)短距离(<10KM)

文章目录 1.1 Bluetooth 1.2 CUAV PW-Link 1.3 ESP8266 wifi telemetry 1.4 ESP32 wifi telemetry 1.5 FrSky telemetry 1.6 Yaapu双向遥测地面站 1.7 HOTT telemetry 1.8 MSP(MultiWii 串行协议)(4.1 版) 1.9 MSP (version 4.2) 1.10 SiK Radio v1 1.11 SiK Radio …

异地电脑文件夹共享吗?

在现代社会中&#xff0c;人们越来越多地需要在不同的地方使用电脑来处理文件和数据。对于那些在异地工作或旅行的人来说&#xff0c;如何共享和访问自己的文件夹成为一个棘手的问题。幸运的是&#xff0c;有一些方法可以帮助我们在异地共享电脑文件夹&#xff0c;并实现远程访…

Window环境下使用go编译grpc最新教程

网上的grpc教程都或多或少有些老或者有些问题&#xff0c;导致最后执行生成文件时会报很多错。这里给出个人实践出可执行的编译命令与碰到的报错与解决方法。&#xff08;ps:本文代码按照煎鱼的教程编写&#xff1a;4.2 gRPC Client and Server - 跟煎鱼学 Go (gitbook.io)&…

【芯片设计- RTL 数字逻辑设计入门 15 -- 函数实现数据大小端转换】

文章目录 函数实现数据大小端转换函数语法函数使用的规则Verilog and Testbench综合图VCS 仿真波形 函数实现数据大小端转换 在数字芯片设计中&#xff0c;经常把实现特定功能的模块编写成函数&#xff0c;在需要的时候再在主模块中调用&#xff0c;以提高代码的复用性和提高设…

专业140+总分420+河海大学863信号与系统考研经验电子信息通信与信息技术,真题,大纲,参考书。

今年的成绩出来倍感欣慰&#xff0c;决定考研的时候并没有想到自己可以考出420的分数&#xff0c;通过自己一年来的努力&#xff0c;成功上岸&#xff0c;期中专业课863信号与系统140接近满分&#xff08;非常感谢信息通信Jenny老师的专业课辅导和平时悉心答疑&#xff0c;不厌…

同步和异步、阻塞与非阻塞

一、同步和异步的概念 首先同步和异步是访问数据的机制 同步&#xff1a;同步一般指主动请求并等待IO操作完成的方式异步&#xff1a;主动请求数据后便可以继续处理其它任务&#xff0c;随后等待IO操作完毕的通知 两者的区别&#xff1a;同步会一行一行执行代码&#xff0c;而…

从模型到前端,你应该知道的LLM生态系统指南

LLM在在2023年发展的风生水起&#xff0c;一个围绕LLM的庞大生态系统正在形成&#xff0c;本文通过介绍这个生态系统的核心组成部分&#xff0c;来详细整理LLM的发展。 模型-核心组件 大型语言模型(llm)是人工智能应用程序背后的原材料。这些模型最初被预先训练来预测句子中的…

NBA2K24 陈盈骏面补

NBA2K23-24 陈盈骏面补 NBA2K23-NBA2K24通用 陈盈骏面补 现效力于中国男子篮球职业联赛CBA广州龙狮 下载地址&#xff1a; https://www.changyouzuhao.cn/9617.html

C语言操作符超详细总结

文章目录 1. 操作符的分类2. 二进制和进制转换2.1 2进制转10进制2.1.1 10进制转2进制数字 2.2 2进制转8进制和16进制2.2.1 2进制转8进制2.2.2 2进制转16进制 3. 原码、反码、补码4.移位操作符4.1 左移操作符4.2 右移操作符 5. 位操作符&#xff1a;&、|、^、~6. 逗号表达式…

windows10安装配置nvm以达到切换nodejs的目的

前言 各种各样的项目&#xff0c;各种node环境&#xff0c;还有node_modules这个庞然大物。。想想都觉得恐怖。 所以现在有了&#xff1a;nvm-切换node环境&#xff0c;pnpm–解决重复下载同样类库的问题。 下面将就如何在win10下配置进行说明 nvm下载配置 nvm的github下载地…

NGINX upstream、stream、四/七层负载均衡以及案例示例

文章目录 前言1. 四/七层负载均衡1.1 开放式系统互联模型 —— OSI1.2 四/七层负载均衡 2. Nginx七层负载均衡2.1 upstream指令2.2 server指令和负载均衡状态与策略2.2.1 负载均衡状态2.2.2 负载均衡策略 2.3 案例 3. Nginx四层负载均衡的指令3.1 stream3.2 upstream指令3.3 四…

Huggingface上传模型

Huggingface上传自己的模型 参考 https://juejin.cn/post/7081452948550746148https://huggingface.co/blog/password-git-deprecationAdding your model to the Hugging Face Hub&#xff0c; huggingface.co/docs/hub/ad…Welcome&#xff0c;huggingface.co/welcome三句指…