this关键字常见用法和含义:
(1)引用当前对象的成员变量:当类的成员变量与方法的参数或局部变量同名时,使用 this
关键字可以明确地指示要引用的是成员变量。例如:
public class Person {
private String name;
public void setName(String name) {
this.name = name;
}
}
在上面的例子中,this.name
引用了类的成员变量 name
,而 name
是方法的参数。这样可以区分变量名,避免歧义。
局部变量:方法内
成员变量:方法外this的作用:区分局部变量和成员变量
this的本质:代表方法调用者的地址值如果不使用this关键字,则变量遵循就近原则,即谁离得近就使用谁的值。
(2)在构造方法中调用其他构造方法:在一个类的构造方法中,可以使用 this 关键字来调用同一个类的其他构造方法。这样可以避免代码重复。例如:
public class Person {
private String name;
private int age;
public Person() {
this("John Doe", 30); // 调用另一个构造方法
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}
在上面的例子中,无参构造方法 Person()
使用 this("John Doe", 30)
调用有参构造方法 Person(String name, int age)
。
(3)返回当前对象:在方法中,可以使用 this 关键字来返回当前对象的引用,这在实现方法链式调用时很常见。例如:
public class Person {
private String name;
private int age;
public Person setName(String name) {
this.name = name;
return this;
}
public Person setAge(int age) {
this.age = age;
return this;
}
}
当在一个方法中使用 return this;
语句时,它表示将当前对象作为方法的返回值返回。在上面的代码中,setName()
和 setAge()
方法都返回 Person
对象,以便允许方法的连续调用。使用这种方法,可以以一种链式的方式设置对象的属性,如 :
Person person = new Person()
.setName("John")
.setAge(30);
泛型是JDK5中引入的特性,它提供了编译时类型安全检测机制
(1)泛型的好处
(2)泛型的定义格式
(3)泛型的作用
如果我们没有给集合指定类型,默认认为所有的数据类型都是Object类型,此时可以往集合添加任意的数据类型。这样带来一个坏处:我们在获取数据的时候,无法使用他的特有行为。
此时推出了泛型,可以在添加数据的时候就把类型进行统一,而且我们在获取数据的时候,也省的强转了,非常的方便。
(4)注意事项
(1)泛型类:在类名后面定义泛型,创建该类对象的时候,确定类型。
修饰符 class 类名<类型>{
}
public class ArrayList<E>{
}
(2)代码示例:
定义泛型类
public class MyArrayList<E> {
// 指定集合默认初始化的长度为10
Object[] obj = new Object[10];
int size;
/**
* E : 表示是不确定的类型。该类型在类名后面已经定义过了。
* e:形参的名字,变量名
*/
public boolean add(E e){
obj[size] = e;
size++;
return true;
}
public E get(int index){
return (E)obj[index];
}
@Override
public String toString() {
return Arrays.toString(obj);
}
}
泛型类测试
public class GenericsDemo02 {
public static void main(String[] args) {
MyArrayList<String> list = new MyArrayList<>();
list.add("aaa");
list.add("bbb");
list.add("ccc");
System.out.println(list.size);
System.out.println(list);
MyArrayList<Integer> list2 = new MyArrayList<>();
list2.add(123);
list2.add(456);
list2.add(789);
int i = list2.get(0);
System.out.println(i);
System.out.println(list2);
}
}
(1)泛型方法:在修饰符后面定义方法,调用该方法的时候,确定类型。
修饰符<类型> 返回值类型 方法名(类型变量名){
}
public<T> void show(T t){
}
public static<T> void show(T t){
}
(2)代码示例
泛型方法
public class ListUtil {
private ListUtil(){}
/**
* 泛型方法
* 类中定义一个静态方法addAll,用来添加多个集合的元素。
* 参数一:集合
* 参数二~最后:要添加的元素
*/
public static<E> void addAll(ArrayList<E> list, E e1, E e2, E e3, E e4){
list.add(e1);
list.add(e2);
list.add(e3);
list.add(e4);
}
/* public static<E> void addAll2(ArrayList<E> list, E...e){
for (E element : e) {
list.add(element);
}
}*/
}
泛型方法测试
public class GenericsDemo03 {
public static void main(String[] args) {
ArrayList<String> list1 = new ArrayList<>();
ListUtil.addAll(list1, "aaa", "bbb", "ccc", "ddd");
System.out.println(list1);
ArrayList<Integer> list2 = new ArrayList<>();
ListUtil.addAll(list2,1,2,3,4);
System.out.println(list2);
}
}
(1)泛型接口:在接口名后面定义泛型,实现类确定类型,实现类延续泛型。
定义
修饰符 interface 接口名<类型>{
}
public interface List<E>{
}
使用
// 实现类给出具体的类型
public class MyList implements List<String> {
}
// 实现类延续泛型,创建实现类对象时再确定类型
public class MyList2<E> implements List<E> {
}
(2)代码示例
实现类:实现类延续泛型,创建实现类对象时再确定类型
public class MyArrayList3<E> implements List<E> {
@Override
public int size() {
return 0;
}
@Override
public boolean isEmpty() {
return false;
}
@Override
public boolean contains(Object o) {
return false;
}
@Override
public Iterator<E> iterator() {
return null;
}
@Override
public Object[] toArray() {
return new Object[0];
}
@Override
public <T> T[] toArray(T[] a) {
return null;
}
@Override
public boolean add(E e) {
return false;
}
@Override
public boolean remove(Object o) {
return false;
}
@Override
public boolean containsAll(Collection<?> c) {
return false;
}
@Override
public boolean addAll(Collection<? extends E> c) {
return false;
}
@Override
public boolean addAll(int index, Collection<? extends E> c) {
return false;
}
@Override
public boolean removeAll(Collection<?> c) {
return false;
}
@Override
public boolean retainAll(Collection<?> c) {
return false;
}
@Override
public void clear() {
}
@Override
public E get(int index) {
return null;
}
@Override
public E set(int index, E element) {
return null;
}
@Override
public void add(int index, E element) {
}
@Override
public E remove(int index) {
return null;
}
@Override
public int indexOf(Object o) {
return 0;
}
@Override
public int lastIndexOf(Object o) {
return 0;
}
@Override
public ListIterator<E> listIterator() {
return null;
}
@Override
public ListIterator<E> listIterator(int index) {
return null;
}
@Override
public List<E> subList(int fromIndex, int toIndex) {
return null;
}
}
实现类:实现类给出具体的类型
public class MyArrayList2 implements List<String> {
@Override
public int size() {
return 0;
}
@Override
public boolean isEmpty() {
return false;
}
@Override
public boolean contains(Object o) {
return false;
}
@Override
public Iterator<String> iterator() {
return null;
}
@Override
public Object[] toArray() {
return new Object[0];
}
@Override
public <T> T[] toArray(T[] a) {
return null;
}
@Override
public boolean add(String s) {
return false;
}
@Override
public boolean remove(Object o) {
return false;
}
@Override
public boolean containsAll(Collection<?> c) {
return false;
}
@Override
public boolean addAll(Collection<? extends String> c) {
return false;
}
@Override
public boolean addAll(int index, Collection<? extends String> c) {
return false;
}
@Override
public boolean removeAll(Collection<?> c) {
return false;
}
@Override
public boolean retainAll(Collection<?> c) {
return false;
}
@Override
public void clear() {
}
@Override
public String get(int index) {
return null;
}
@Override
public String set(int index, String element) {
return null;
}
@Override
public void add(int index, String element) {
}
@Override
public String remove(int index) {
return null;
}
@Override
public int indexOf(Object o) {
return 0;
}
@Override
public int lastIndexOf(Object o) {
return 0;
}
@Override
public ListIterator<String> listIterator() {
return null;
}
@Override
public ListIterator<String> listIterator(int index) {
return null;
}
@Override
public List<String> subList(int fromIndex, int toIndex) {
return null;
}
}
实现类对象
public class GenericsDemo04 {
public static void main(String[] args) {
/**
* 泛型接口的两种使用方式:
* 1.实现类给出具体的类型
* 2.实现类延续泛型,创建实现类对象时再确定类型
*/
// 实现类给出具体的类型
MyArrayList2 list = new MyArrayList2();
// 实现类延续泛型,创建实现类对象时再确定类型
MyArrayList3<String> list2 = new MyArrayList3<>();
MyArrayList3<Integer> list3 = new MyArrayList3<>();
// 之后的操作和操作普通list类似,只不过实现类中还没实现具体的方法
}
}
需求:定义一个方法,方法的参数类型为不确定类型,但是希望只能传递Ye Fu Zi类型的数据,不能传递Student类型的数据
(1)仅使用泛型,不使用泛型通配符时
泛型里面写的是什么类型,那么只能传递什么类型的数据。而如果泛型方法设置为类型为T时,就又可以接受任意的数据类型,不符合要求。
(2)使用泛型通配符可以解决这个需求
? 不仅可以表示不确定的类型,还可以进行类型的限定
? extends E: 表示可以传递E或者E所有的子类类型
? super E:表示可以传递E或者E所有的父类类型
(3)泛型通配符应用场景:
关键点:可以限定类型的范围。
(4)代码示例
public class GenericsDemo06 {
public static void main(String[] args) {
//创建集合的对象
ArrayList<Ye> list1 = new ArrayList<>();
ArrayList<Fu> list2 = new ArrayList<>();
ArrayList<Zi> list3 = new ArrayList<>();
ArrayList<Student> list4 = new ArrayList<>();
method(list1);
method(list2);
//method(list3); // 报错
//method(list4); // 报错
}
// 可以传递Fu及Fu的所有父类类型
public static void method(ArrayList<? super Fu> list) {
}
}
class Ye {
}
class Fu extends Ye {
}
class Zi extends Fu {
}
class Student{}
格式:
属性类型...名字
int...args
public class ArgsDemo3 {
public static void main(String[] args) {
int sum = getSum(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
System.out.println(sum);
}
public static int getSum(int...args){
int sum = 0;
for (int i : args) {
sum = sum + i;
}
return sum;
}
}
把第三方的代码导入到当前的项目当中
新建lib文件夹,把jar粘贴到lib文件夹当中,全选后右键点击选择add as library
检测导入成功:导入成功后jar包可以展开。在项目重构界面可以看到导入的内容
把配置文件粘贴到src文件夹下
在代码中获取日志对象
调用方法打印日志
TRACE, DEBUG, INFO, WARN, ERROR
还有两个特殊的:
日志级别从小到大的关系:TRACE < DEBUG < INFO < WARN < ERROR
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<!--
CONSOLE :表示当前的日志信息是可以输出到控制台的。
-->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<!--输出流对象 默认 System.out 改为 System.err-->
<target>System.out</target>
<encoder>
<!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度
%msg:日志消息,%n是换行符-->
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%-5level] %c [%thread] : %msg%n</pattern>
</encoder>
</appender>
<!-- File是输出的方向通向文件的 -->
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
<charset>utf-8</charset>
</encoder>
<!--日志输出路径-->
<file>C:/code/itheima-data.log</file>
<!--指定日志文件拆分和压缩规则-->
<rollingPolicy
class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!--通过指定压缩文件名称,来确定分割文件方式-->
<fileNamePattern>C:/code/itheima-data2-%d{yyyy-MMdd}.log%i.gz</fileNamePattern>
<!--文件拆分大小-->
<maxFileSize>1MB</maxFileSize>
</rollingPolicy>
</appender>
<!--
level:用来设置打印级别,大小写无关:TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF
, 默认debug
<root>可以包含零个或多个<appender-ref>元素,标识这个输出位置将会被本日志级别控制。
-->
<root level="info">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="FILE" />
</root>
</configuration>
作用:负责将.class文件(存储的物理文件)加载在到内存中
简单理解:字节码文件什么时候会被加载到内存中?
有以下的几种情况:
总结而言:用到了就加载,不用不加载
(1)加载
(2)链接
验证
确保Class文件字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身安全
(文件中的信息是否符合虚拟机规范有没有安全隐患)
准备
负责为类的类变量(被static修饰的变量)分配内存,并设置默认初始化值
(初始化静态变量)
解析
将类的二进制数据流中的符号引用替换为直接引用
(本类中如果用到了其他类,此时就需要找到对应的类)
(3)初始化
根据程序员通过程序制定的主观计划去初始化类变量和其他资源
(静态变量赋值以及初始化其他资源)
- 当一个类被使用的时候,才会加载到内存
- 类加载的过程: 加载、验证、准备、解析、初始化
(1)分类
(2)类加载器的继承关系
(3)代码演示
public class ClassLoaderDemo1 {
public static void main(String[] args) {
//获取系统类加载器
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
//获取系统类加载器的父加载器 --- 平台类加载器
ClassLoader classLoader1 = systemClassLoader.getParent();
//获取平台类加载器的父加载器 --- 启动类加载器
ClassLoader classLoader2 = classLoader1.getParent();
System.out.println("系统类加载器" + systemClassLoader);
System.out.println("平台类加载器" + classLoader1);
System.out.println("启动类加载器" + classLoader2);
}
}
(1)介绍
如果一个类加载器收到了类加载请求,它并不会自己先去加载,而是把这个请求委托给父类的加载器去执行,如果父类加载器还存在其父类加载器,则进一步向上委托,依次递归,请求最终将到达顶层的启动类加载器,如果父类加载器可以完成类加载任务,就成功返回,倘若父类加载器无法完成此加载任务,子加载器才会尝试自己去加载,这就是双亲委派模式
(1)方法介绍
方法名 | 说明 |
---|---|
public static ClassLoader getSystemClassLoader() | 获取系统类加载器 |
public InputStream getResourceAsStream(String name) | 加载某一个资源文件 |
(2)示例代码
public class ClassLoaderDemo2 {
public static void main(String[] args) throws IOException {
//static ClassLoader getSystemClassLoader() 获取系统类加载器
//InputStream getResourceAsStream(String name) 加载某一个资源文件
//获取系统类加载器
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
//利用加载器去加载一个指定的文件
//参数:文件的路径(放在src的根目录下,默认去那里加载)
//返回值:字节流。
InputStream is = systemClassLoader.getResourceAsStream("prop.properties");
Properties prop = new Properties();
prop.load(is);
System.out.println(prop);
is.close();
}
}
共同点:都可以对程序进行解释说明。
不同点:
举例:子类重写父类方法的时候,在重写的方法上面写@Override。当虚拟机看到@Override的时候,就知道下面的方法是重写的父类的。检查语法,如果语法正确编译正常,如果语法错误,就会报错。
@Override:表示方法的重写
@Deprecated:表示修饰的方法已过时
@SuppressWarnings(“all”):压制警告
除此之外,还需要掌握第三方框架中提供的注解,比如在Junit中:
@Test 表示运行测试方法
@Before 表示在Test之前运行,进行数据的初始化
@After 表示在Test之后运行,进行数据的还原
自定义注解单独存在是没有什么意义的,一般会跟反射结合起来使用,会用发射去解析注解。
关于注解的解析,一般是在框架的底层已经写好了。
value:当注解中只有“一个属性“,并且属性名是“value“,使用注解时,可以省略value属性名。
//注解的定义
public @interface Anno2 {
public String value();
public int age() default 23;
}
//注解的使用
@Anno2("123")
public class AnnoDemo2 {
@Anno2("123")
public void method(){
}
}
元注解:可以写在注解上面的注解,如:
(1)Target
? 作用:用来标识注解使用的位置,如果没有使用该注解标识,则自定义的注解可以使用在任意位置。
? 可使用的值定义在ElementType枚举类中,常用值如下
(2)Retention
? 作用:用来标识注解的生命周期(有效范围)
? 可使用的值定义在RetentionPolicy枚举类中,常用值如下
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyTest {
}
public class MyTestMethod {
@MyTest
public void method1(){
System.out.println("method1");
}
public void method2(){
System.out.println("method2");
}
@MyTest
public void method3(){
System.out.println("method3");
}
}
public class MyTestDemo {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, InvocationTargetException {
//1,获取class对象
Class clazz = Class.forName("com.itheima.test2.MyTestMethod");
//获取对象
Object o = clazz.newInstance();
//2.获取所有方法
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {
//method依次表示类里面的每一个方法
method.setAccessible(true);
//判断当前方法有没有MyTest注解
if(method.isAnnotationPresent(MyTest.class)){
method.invoke(o);
}
}
}
}
将一个类A定义在另一个类B里面,里面的那个类A就称为内部类,B则称为外部类。可以把内部类理解成寄生,外部类理解成宿主。
一个事物内部还有一个独立的事物,内部的事物脱离外部的事物无法独立使用
按定义的位置来分:
(1)成员内部内:类定义在了成员位置 (类中方法外称为成员位置,无static修饰的内部类)
(2)静态内部类:类定义在了成员位置 (类中方法外称为成员位置,有static修饰的内部类)
(3)局部内部类:类定义在方法内
(4)匿名内部类:没有名字的内部类,可以在方法中,也可以在类中方法外。
(1)成员内部类特点
(2)内部类的使用格式
外部类.内部类。 // 访问内部类的类型都是用 外部类.内部类
(3)获取成员内部类对象的两种方式
方式一:外部直接创建成员内部类的对象
外部类.内部类 变量 = new 外部类().new 内部类();
方式二:在外部类中定义一个方法提供内部类的对象
(4)代码演示
方式一
public class Test {
public static void main(String[] args) {
// 宿主:外部类对象。
// Outer out = new Outer();
// 创建内部类对象。
Outer.Inner oi = new Outer().new Inner();
oi.method();
}
}
class Outer {
// 成员内部类,属于外部类对象的。
// 拓展:成员内部类不能定义静态成员。
public class Inner{
// 这里面的东西与类是完全一样的。
public void method(){
System.out.println("内部类中的方法被调用了");
}
}
}
方式二
public class Outer {
String name;
private class Inner{
static int a = 10;
}
public Inner getInstance(){
return new Inner();
}
}
public class Test {
public static void main(String[] args) {
Outer o = new Outer();
System.out.println(o.getInstance());
}
}
编写成员内部类的注意点:
详解:
请在?处填写相应的代码,以达到输出的内容
内部类访问外部类对象的格式是:外部类名.this
public class Test {
public static void main(String[] args) {
Outer.inner oi = new Outer().new inner();
oi.method();
}
}
class Outer { // 外部类
private int a = 30;
// 在成员位置定义一个类
class inner {
private int a = 20;
public void method() {
int a = 10;
System.out.println(???); // 10 答案:a
System.out.println(???); // 20 答案:this.a
System.out.println(???); // 30 答案:Outer.this.a
}
}
}
(1)静态内部类特点
外部类.内部类
。(2)内部类的使用格式
外部类.内部类
(3)静态内部类对象的创建格式
外部类.内部类 变量 = new 外部类.内部类构造器;
(4)调用方法的格式
外部类名.内部类名.方法名()
;(5)代码示例
// 外部类:Outer01
class Outer01{
private static String sc_name = "黑马程序";
// 内部类: Inner01
public static class Inner01{
// 这里面的东西与类是完全一样的。
private String name;
public Inner01(String name) {
this.name = name;
}
public void showName(){
System.out.println(this.name);
// 拓展:静态内部类可以直接访问外部类的静态成员。
System.out.println(sc_name);
}
}
}
public class InnerClassDemo01 {
public static void main(String[] args) {
// 创建静态内部类对象。
// 外部类.内部类 变量 = new 外部类.内部类构造器;
Outer01.Inner01 in = new Outer01.Inner01("张三");
in.showName();
}
}
局部内部类:定义在方法中的类。
定义格式:
class 外部类名 {
数据类型 变量名;
修饰符 返回值类型 方法名(参数列表) {
// …
class 内部类 {
// 成员变量
// 成员方法
}
}
}
匿名内部类:是内部类的简化写法,是一个隐含了名字的内部类。
匿名内部类必须继承一个父类或者实现一个父接口。
匿名内部类的特点:
(1)定义一个没有名字的内部类
(2)这个类实现了父类,或者父类接口
(3)匿名内部类会创建这个没有名字的类的对象
new 类名或者接口名() {
重写方法;
};
new 父类名或者接口名(){
// 方法重写
@Override
public void method() {
// 执行语句
}
};
包含了:
继承或者实现关系
方法重写
创建对象
所以从语法上来讲,这个整体其实是匿名内部类对象。
如果希望定义一个只要使用一次的类,就可考虑使用匿名内部类。匿名内部类的本质作用是为了简化代码。
之前使用接口时,似乎得做如下几步操作:
interface Swim {
public abstract void swimming();
}
// 1. 定义接口的实现类
class Student implements Swim {
// 2. 重写抽象方法
@Override
public void swimming() {
System.out.println("狗刨式...");
}
}
public class Test {
public static void main(String[] args) {
// 3. 创建实现类对象
Student s = new Student();
// 4. 调用方法
s.swimming();
}
}
而最终的目的只是为了调用方法,所以可以使用匿名内部类,把以上四步合成一步。
以接口为例,匿名内部类的使用,代码如下:
interface Swim {
public abstract void swimming();
}
public class Demo07 {
public static void main(String[] args) {
// 使用匿名内部类
new Swim() {
@Override
public void swimming() {
System.out.println("自由泳...");
}
}.swimming();
// 接口 变量 = new 实现类(); // 多态,走子类的重写方法
Swim s2 = new Swim() {
@Override
public void swimming() {
System.out.println("蛙泳...");
}
};
s2.swimming();
}
}
通常在方法的形式参数是接口或者抽象类时,也可以将匿名内部类作为参数传递。
举例一:
interface Swim {
public abstract void swimming();
}
public class Demo07 {
public static void main(String[] args) {
// 方法1
// 匿名内部类使用场景:作为方法参数传递
Swim s = new Swim() {
@Override
public void swimming() {
System.out.println("蝶泳...");
}
};
// 传入匿名内部类
goSwimming(s);
// 方法2:一步到位
goSwimming(new Swim() {
public void swimming() {
System.out.println("蛙泳...");
}
});
}
// 定义一个方法,方法的形式参数是一个接口
public static void goSwimming(Swim s) {
s.swimming();
}
}
举例二:
interface MyInterface {
void myMethod();
}
class MyClass {
void processInterface(MyInterface myInterface) {
myInterface.myMethod();
}
}
public class Main {
public static void main(String[] args) {
MyClass myClass = new MyClass();
// 通过匿名内部类实现接口并传递给方法
myClass.processInterface(new MyInterface() {
@Override
public void myMethod() {
System.out.println("Implementation of myMethod in anonymous inner class");
}
});
}
}