JavaSE简单总结

一. 基础编程

基础语法(这里不再概述)

面向对象

面向对象是JavaSE中的灵魂,高级编程中的每一项其实就是拿来一个类,接口等让你使用,因此面向对象是重中之重。

类与对象

类:对一类事物的描述,是抽象的、概念上的定义
对象:是实际存在的该类事物的每个个体,因而也称为实例(instance)

面向对象程序设计的重点是类的设计
设计类,就是设计类的成员。

二者的关系:对象,是由类new出来的,派生出来的。

面向过程:强调的是功能行为,以函数为最小单位,考虑怎么做。
面向对象:强调具备了功能的对象,以类/对象为最小单位,考虑谁来做。

面向对象可以想为一个大公司,面向过程可以想为一个小公司,即小公司对于接到的活都是一起办了,即是一种面向过程的思想;
大公司不能再向小公司一样对于接到的活所有人一起去解决,那样太耗费时间了,并且容易乱,这个时候大公司就会将公司分成好几个部门,
每一个部门专门应对某些事情,这样即为封装为类的一种思想,且如果想对一个部门添加一些新功能直接给他加上新的属性方法即可。

面向对象思想落地实现的规则

  • 1.创建类,设计类的成员
  • 2.创建类的对象
  • 3.通过“对象.属性”或“对象.方法”调用对象的结构

对象的创建与对象的内存解析
典型代码:

Person p1 = new Person();
Person p2 = new Person();
Person p3 = p1;//没有新创建一个对象,共用一个堆空间中的对象实体。

说明:
如果创建了一个类的多个对象,则每个对象都独立的拥有一套类的属性。(非static的)
意味着:如果我们修改一个对象的属性a,则不影响另外一个对象属性a的值。

类的结构

1.属性
对比:属性 vs 局部变量

相同点:

  •  1.1  定义变量的格式:数据类型  变量名 = 变量值
    
  •  1.2 先声明,后使用
    
  •  1.3 变量都其对应的作用域 
    

不同点:

  •  1.1   在类中声明的位置的不同
    
  •  1.2 关于权限修饰符的不同
    
  •  1.3 有默认初始化值
    

2.方法

方法:描述类应该具的功能。

方法的声明:

权限修饰符  返回值类型  方法名(形参列表){
    
    
				方法体
}

3.构造器

构造器的作用:

  • 1.创建对象
  • 2.初始化对象的信息

使用说明:

  • 1.如果没显式的定义类的构造器的话,则系统默认提供一个空参的构造器
  • 2.定义构造器的格式:权限修饰符 类名(形参列表){}
  • 3.一个类中定义的多个构造器,彼此构成重载
  • 4.一旦我们显式的定义了类的构造器之后,系统就不再提供默认的空参构造器
  • 5.一个类中,至少会有一个构造器。

三大特征

1.封装性
隐藏对象内部的复杂性,只对外公开简单的接口。便于外界调用,从而提高系统的可扩展性、可维护性。通俗的说,把该隐藏的隐藏起来,该暴露的暴露出来。这就是封装性的设计思想。

当我们创建一个类的对象以后,我们可以通过"对象.属性"的方式,对对象的属性进行赋值。这里,赋值操作要受到属性的数据类型和存储范围的制约。除此之外,没其他制约条件。但是,在实际问题中,我们往往需要给属性赋值加入额外的限制条件。这个条件就不能在属性声明时体现,我们只能通过方法进行限制条件的添加。(比如:setLegs()同时,我们需要避免用户再使用"对象.属性"的方式对属性进行赋值。则需要将属性声明为私有的(private).

体现一:将类的属性xxx私化(private),同时,提供公共的(public)方法来获取(getXxx)和设置(setXxx)此属性的值

private double radius;
public void setRadius(double radius){
    
    
	this.radius = radius;
}
public double getRadius(){
    
    
	return radius;
}

体现二:不对外暴露的私有的方法
体现三:单例模式(将构造器私有化)
体现四:如果不希望类在包外被调用,可以将类设置为缺省的。

2.继承性
1.为什么要有类的继承性?

  • ① 减少了代码的冗余,提高了代码的复用性
  • ② 便于功能的扩展
  • ③ 为之后多态性的使用,提供了前提

2.继承性的格式:

class A extends B{
    
    }
     A:子类、派生类、subclass
     B:父类、超类、基类、superclass

3.子类继承父类以后有哪些不同?
体现:一旦子类A继承父类B以后,子类A中就获取了父类B中声明的所有的属性和方法。
特别的,父类中声明为private的属性或方法,子类继承父类以后,仍然认为获取了父类中私的结构。只因为封装性的影响,使得子类不能直接调用父类的结构而已。

子类继承父类以后,还可以声明自己特有的属性或方法:实现功能的拓展。
子类和父类的关系,不同于子集和集合的关系。
extends:延展、扩展

4.Java中继承性的说明

  • 1.一个类可以被多个子类继承。
  • 2.Java中类的单继承性:一个类只能有一个父类
  • 3.子父类是相对的概念。
  • 4.子类直接继承的父类,称为:直接父类。间接继承的父类称为:间接父类
  • 5.子类继承父类以后,就获取了直接父类以及所间接父类中声明的属性和方法

5.Object类的理解

  1. 如果我们没显式的声明一个类的父类的话,则此类继承于java.lang.Object类
  2. 所的java类(除java.lang.Object类之外都直接或间接的继承于java.lang.Object类
  3. 意味着,所的java类具有java.lang.Object类声明的功能。

3.多态性

1.多态性的理解:可以理解为一个事物的多种形态。
2.何为多态性:
对象的多态性:父类的引用指向子类的对象(或子类的对象赋给父类的引用)

3.举例:

Person p = new Man();
Object obj = new Date();

即父类的引用指向子类的对象!

4.多态的应用
在这里插入图片描述

即我们可以理解为对于方法的输入类型有若干类型时,若其满足有同一父类,我们可以使用其父类来做参数类型,即用其父类的引用来指向子类的对象。
而同时由于我们是先用其父类来“代表”了一下其各个子类,在方法内部的进行对输入类型的方法调用时,由于我们是拿到的父类,因此这里必然是不能用其各个子类的方法的;
并且我们是拿到的父类,但是最终参数输入后我们是用父类的引用来指向子类,因此使用的父类的方法换成了子类重写的方法。
即你可以理解为通过多态,将父类的引用指向了子类的对象,即你相当于就是拿到了子类对象自己。但是由于你开始时的“容器”是只按照了父类的形状等设置的,因此你拿到的子类对象想要真正使用时,即要装进“容器”时发现装不完,即他作为子类多出来的别的功能放不进去的,因此又被舍弃了,所以拿到的就还是父类本身,不过其方法若被重写的话是会保留的,因为其将原来父类的覆盖了,所以能放进“容器”中。

总结
一种代码呈现多种形态,省去了多种重载方法的设计了;
对象的多态性–对象以多种形态呈现;

5.equals方法其实就是多态性的一个实例:
equals方法就是多态性的一个实例,若其对每一种引用类型都写一个equals方法,太过繁杂且逻辑几乎重复,于是我们使用所有引用类型的祖宗Object类来先作为参数,将此种方法写好,这样的话后面可以自行根据输入的对象类型而调到不同的类型的吃和叫方法,即减少了代码量。

权限修饰符

Java规定的四种权限修饰符
权限从小到大顺序为:private < 缺省 < protected < public
具体的修饰范围:
在这里插入图片描述

权限修饰符可用来修饰的结构说明:
4种权限都可以用来修饰类的内部结构:属性、方法、构造器、内部类
修饰类的话,只能使用:缺省、public

接口与抽象类

接口

1.接口使用interface来定义
2.Java中,接口和类是并列的两个结构
3.如何定义接口:定义接口中的成员

3.1 JDK7及以前:只能定义全局常量和抽象方法
全局常量:public static final的.但是书写时,可以省略不写
抽象方法:public abstract的

3.2 JDK8:除了定义全局常量和抽象方法之外,还可以定义静态方法、默认方法

4.接口中不能定义构造器的!意味着接口不可以实例化

5.Java开发中,接口通过让类去实现(implements)的方式来使用.
如果实现类覆盖了接口中的所抽象方法,则此实现类就可以实例化
如果实现类没覆盖接口中所的抽象方法,则此实现类仍为一个抽象类

6.Java类可以实现多个接口 —>弥补了Java单继承性的局限性
格式:class AA extends BB implements CC,DD,EE

7.接口与接口之间可以继承,而且可以多继承

8.接口的具体使用,体现多态性

9.接口,实际上可以看做是一种规范

抽象类与抽象方法
abstract: 抽象的
1.可以用来修饰:类、方法

2.具体的:

abstract修饰类:抽象类

此类不能实例化
抽象类中一定有构造器,便于子类实例化时调用(涉及:子类对象实例化的全过程)
开发中,都会提供抽象类的子类,让子类对象实例化,完成相关的操作 —>抽象的使用前提:继承性

abstract修饰方法:抽象方法

抽象方法只方法的声明,没方法体
包含抽象方法的类,一定是一个抽象类。反之,抽象类中可以没有抽象方法的。
若子类重写了父类中的所的抽象方法后,此子类方可实例化
若子类没重写父类中的所的抽象方法,则此子类也是一个抽象类,需要使用abstract修饰

3.注意点:

  • 1.abstract不能用来修饰:属性、构造器等结构
  • 2.abstract不能用来修饰私方法、静态方法、final的方法、final的类

关键字的使用与含义

1.this
this理解为:当前对象 或 当前正在创建的对象,可以调用的结构:属性、方法;构造器。

2.package,import
使用package声明类或接口所属的包,声明在源文件的首行;
在源文件中显式的使用import结构导入指定包下的类、接口;

3.super
super 关键字可以简单理解为:父类的。可以用来调用的结构:属性、方法、构造器。

4.static
static:静态的
可以用来修饰的结构:主要用来修饰类的内部结构
属性、方法、代码块、内部类
static修饰属性的其他说明:
① 静态变量随着类的加载而加载。可以通过"类.静态变量"的方式进行调用
② 静态变量的加载要早于对象的创建。
③ 由于类只会加载一次,则静态变量在内存中也只会存在一份:存在方法区的静态域中。

5.final
final:最终的
可以用来修饰:类、方法、变量
用来修饰一个类:此类不能被其他类所继承。用来修饰方法:表明此方法不可以被重写。用来修饰变量:此时的"变量"就称为是一个常量

异常处理

异常处理的方式

异常处理方式一:try-catch-finally
使用说明:

try{
    
    
		//可能出现异常的代码
}catch(异常类型1 变量名1){
    
    
 		//处理异常的方式1
}catch(异常类型2 变量名2){
    
    
 		//处理异常的方式2
}catch(异常类型3 变量名3){
    
    
 		//处理异常的方式3
 }
....
finally{
    
    
 		//一定会执行的代码
 }

说明:

  1. finally是可的。
  2. 使用try将可能出现异常代码包装起来,在执行过程中,一旦出现异常,就会生成一个对应异常类的对象,根据此对象的类型,去catch中进行匹配
  3. 一旦try中的异常对象匹配到某一个catch时,就进入catch中进行异常的处理。一旦处理完成,就跳出当前的try-catch结构(在没写finally的情况。继续执行其后的代码
  4. catch中的异常类型如果没子父类关系,则谁声明在上,谁声明在下无所谓。
    catch中的异常类型如果满足子父类关系,则要求子类一定声明在父类的上面。否则报错
  5. 常用的异常对象处理的方式: ① String getMessage() ② printStackTrace()
  6. 在try结构中声明的变量,再出了try结构以后,就不能再被调用
  7. try-catch-finally结构可以嵌套

异常处理方式二:
"throws + 异常类型"写在方法的声明处。指明此方法执行时,可能会抛出的异常类型。
一旦当方法体执行时,出现异常,仍会在异常代码处生成一个异常类的对象,此对象满足throws后异常类型时,就会被抛出。异常代码后续的代码,就不再执行!

对比两种处理方式

try-catch-finally:真正的将异常给处理掉了。
throws的方式只是将异常抛给了方法的调用者。并没真正将异常处理掉。

自定义异常类

如何自定义异常类?

  1. 继承于现的异常结构:RuntimeException 、Exception
  2. 提供全局常量:serialVersionUID
  3. 提供重载的构造器
public class MyException extends Exception{
    
    
	
	static final long serialVersionUID = -7034897193246939L;
	
	public MyException(){
    
    
		
	}
	
	public MyException(String msg){
    
    
		super(msg);
	}
}

二. 高级编程

多线程

程序,进程,线程的理解

  1. 程序(programm)
    概念:是为完成特定任务、用某种语言编写的一组指令的集合。即指一段静态的代码。

  2. 进程(process)
    概念:程序的一次执行过程,或是正在运行的一个程序。
    说明:进程作为资源分配的单位,系统在运行时会为每个进程分配不同的内存区域

  3. 线程(thread)
    概念:进程可进一步细化为线程,是一个程序内部的一条执行路径。
    说明:线程作为调度和执行的单位,每个线程拥独立的运行栈和程序计数器(pc),线程切换的开销小。

并行与并发

并行与并发的理解
并行:多个CPU同时执行多个任务。比如:多个人同时做不同的事。
并发:一个CPU(采用时间片)同时执行多个任务。比如:秒杀、多个人做同一件事

创建线程的4种方式

方式一:继承Thread类的方式:

  1. 创建一个继承于Thread类的子类
  2. 重写Thread类的run() --> 将此线程执行的操作声明在run()中
  3. 创建Thread类的子类的对象
  4. 通过此对象调用start():①启动当前线程 ② 调用当前线程的run()

方式二:实现Runnable接口的方式:

  1. 创建一个实现了Runnable接口的类
  2. 实现类去实现Runnable中的抽象方法:run()
  3. 创建实现类的对象
  4. 将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象
  5. 通过Thread类的对象调用start()

新增方式一:实现Callable接口。 — JDK 5.0新增

//1.创建一个实现Callable的实现类
class NumThread implements Callable{
    
    
    //2.实现call方法,将此线程需要执行的操作声明在call()中
    @Override
    public Object call() throws Exception {
    
    
        int sum = 0;
        for (int i = 1; i <= 100; i++) {
    
    
            if(i % 2 == 0){
    
    
                System.out.println(i);
                sum += i;
            }
        }
        return sum;
    }
}


public class ThreadNew {
    
    
    public static void main(String[] args) {
    
    
        //3.创建Callable接口实现类的对象
        NumThread numThread = new NumThread();
        //4.将此Callable接口实现类的对象作为传递到FutureTask构造器中,创建FutureTask的对象
        FutureTask futureTask = new FutureTask(numThread);
        //5.将FutureTask的对象作为参数传递到Thread类的构造器中,创建Thread对象,并调用start()
        new Thread(futureTask).start();

        try {
    
    
            //6.获取Callable中call方法的返回值
            //get()返回值即为FutureTask构造器参数Callable实现类重写的call()的返回值。
            Object sum = futureTask.get();
            System.out.println("总和为:" + sum);
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        } catch (ExecutionException e) {
    
    
            e.printStackTrace();
        }
    }

}

新增方式二:使用线程池

class NumberThread implements Runnable{
    
    

    @Override
    public void run() {
    
    
        for(int i = 0;i <= 100;i++){
    
    
            if(i % 2 == 0){
    
    
                System.out.println(Thread.currentThread().getName() + ": " + i);
            }
        }
    }
}

class NumberThread1 implements Runnable{
    
    

    @Override
    public void run() {
    
    
        for(int i = 0;i <= 100;i++){
    
    
            if(i % 2 != 0){
    
    
                System.out.println(Thread.currentThread().getName() + ": " + i);
            }
        }
    }
}

public class ThreadPool {
    
    

    public static void main(String[] args) {
    
    
        //1. 提供指定线程数量的线程池
        ExecutorService service = Executors.newFixedThreadPool(10);
        ThreadPoolExecutor service1 = (ThreadPoolExecutor) service;
        //设置线程池的属性
//        System.out.println(service.getClass());
//        service1.setCorePoolSize(15);
//        service1.setKeepAliveTime();


        //2.执行指定的线程的操作。需要提供实现Runnable接口或Callable接口实现类的对象
        service.execute(new NumberThread());//适合适用于Runnable
        service.execute(new NumberThread1());//适合适用于Runnable

//        service.submit(Callable callable);//适合使用于Callable
        //3.关闭连接池
        service.shutdown();
    }

}

Thread类

Thread类中的常用的方法:

  1. start():启动当前线程;调用当前线程的run()
  2. run(): 通常需要重写Thread类中的此方法,将创建的线程要执行的操作声明在此方法中
  3. currentThread():静态方法,返回执行当前代码的线程
  4. getName():获取当前线程的名字
  5. setName():设置当前线程的名字
  6. yield():释放当前cpu的执行权
  7. join():在线程a中调用线程b的join(),此时线程a就进入阻塞状态,直到线程b完全执行完以后,线程a才结束阻塞状态。
  8. stop():已过时。当执行此方法时,强制结束当前线程。
  9. sleep(long millitime):让当前线程“睡眠”指定的millitime毫秒。在指定的millitime毫秒时间内,当前线程是阻塞状态。
  10. isAlive():判断当前线程是否存活

Thread的生命周期
在这里插入图片描述
说明:
1.生命周期关注两个概念:状态、相应的方法
2.关注:状态a–>状态b:哪些方法执行了(回调方法)
某个方法主动调用:状态a–>状态b
3.阻塞:临时状态,不可以作为最终状态
死亡:最终状态。

线程的同步机制

在Java中,我们通过同步机制,来解决线程的安全问题。

方式一:同步代码块

 synchronized(同步监视器){
    
    
     //需要被同步的代码

  }

说明:
1.操作共享数据的代码,即为需要被同步的代码。 -->不能包含代码多了,也不能包含代码少了。
2.共享数据:多个线程共同操作的变量。比如:ticket就是共享数据。
3.同步监视器,俗称:锁。任何一个类的对象,都可以充当锁。
要求:多个线程必须要共用同一把锁。

补充:在实现Runnable接口创建多线程的方式中,我们可以考虑使用this充当同步监视器。
在继承Thread类创建多线程的方式中,慎用this充当同步监视器,考虑使用当前类充当同步监视器。

方式二:同步方法
如果操作共享数据的代码完整的声明在一个方法中,我们不妨将此方法声明同步的。

关于同步方法的总结:

  1. 同步方法仍然涉及到同步监视器,只是不需要我们显式的声明。
  2. 非静态的同步方法,同步监视器是:this
    静态的同步方法,同步监视器是:当前类本身

方式三:Lock锁 — JDK5.0新增

面试题:synchronized 与 Lock的异同?

相同:二者都可以解决线程安全问题
不同:synchronized机制在执行完相应的同步代码以后,自动的释放同步监视器
Lock需要手动的启动同步(lock(),同时结束同步也需要手动的实现(unlock())

使用的优先顺序:
Lock —> 同步代码块(已经进入了方法体,分配了相应资源 ) —> 同步方法(在方法体之外)

线程的通信

1.线程通信涉及到的三个方法:

  • wait():一旦执行此方法,当前线程就进入阻塞状态,并释放同步监视器。
  • notify():一旦执行此方法,就会唤醒被wait的一个线程。如果有多个线程被wait,就唤醒优先级高的那个。
  • notifyAll():一旦执行此方法,就会唤醒所有被wait的线程。

2.说明:

  • 1.wait(),notify(),notifyAll()三个方法必须使用在同步代码块或同步方法中。
  • 2.wait(),notify(),notifyAll()三个方法的调用者必须是同步代码块或同步方法中的同步监视器。
  • 否则,会出现IllegalMonitorStateException异常
  • 3.wait(),notify(),notifyAll()三个方法是定义在java.lang.Object类中

常用类

包装类

1.为什么要有包装类(或封装类)
为了使基本数据类型的变量具有类的特征,引入包装类。

2.基本数据类型与对应的包装类:
在这里插入图片描述

3.需要掌握的类型间的转换:(基本数据类型、包装类、String)

在这里插入图片描述

简易版:

基本数据类型<—>包装类:JDK 5.0 新特性:自动装箱 与自动拆箱
基本数据类型、包装类—>String:调用String重载的valueOf(Xxx xxx)
String—>基本数据类型、包装类:调用包装类的parseXxx(String s)
注意:转换时,可能会报NumberFormatException

String类

1.概述
String:字符串,使用一对""引起来表示。

  • String声明为final的,不可被继承
  • String实现了Serializable接口:表示字符串是支持序列化的。
    实现了Comparable接口:表示String可以比较大小
  • String内部定义了final char[] value用于存储字符串数据
  • 通过字面量的方式(区别于new给一个字符串赋值,此时的字符串值声明在字符串常量池中)。
  • 字符串常量池中是不会存储相同内容(使用String类的equals()比较,返回true)的字符串的。

2.String的不可变性

  • 1.当对字符串重新赋值时,需要重写指定内存区域赋值,不能使用原有的value进行赋值。
  • 2.当对现的字符串进行连接操作时,也需要重新指定内存区域赋值,不能使用原有的value进行赋值。
  • 3.当调用String的replace()方法修改指定字符或字符串时,也需要重新指定内存区域赋值,不能使用原有的value进行赋值。

3.常用方法:

int length():返回字符串的长度: return value.length
char charAt(int index): 返回某索引处的字符return value[index]
boolean isEmpty():判断是否是空字符串:return value.length == 0
String toLowerCase():使用默认语言环境,将 String 中的所字符转换为小写
String toUpperCase():使用默认语言环境,将 String 中的所字符转换为大写
String trim():返回字符串的副本,忽略前导空白和尾部空白
boolean equals(Object obj):比较字符串的内容是否相同
boolean equalsIgnoreCase(String anotherString):与equals方法类似,忽略大小写
String concat(String str):将指定字符串连接到此字符串的结尾。 等价于用“+int compareTo(String anotherString):比较两个字符串的大小
String substring(int beginIndex):返回一个新的字符串,它是此字符串的从beginIndex开始截取到最后的一个子字符串。
String substring(int beginIndex, int endIndex) :返回一个新字符串,它是此字符串从beginIndex开始截取到endIndex(不包含)的一个子字符串。

boolean endsWith(String suffix):测试此字符串是否以指定的后缀结束
boolean startsWith(String prefix):测试此字符串是否以指定的前缀开始
boolean startsWith(String prefix, int toffset):测试此字符串从指定索引开始的子字符串是否以指定前缀开始

boolean contains(CharSequence s):当且仅当此字符串包含指定的 char 值序列时,返回 true
int indexOf(String str):返回指定子字符串在此字符串中第一次出现处的索引
int indexOf(String str, int fromIndex):返回指定子字符串在此字符串中第一次出现处的索引,从指定的索引开始
int lastIndexOf(String str):返回指定子字符串在此字符串中最右边出现处的索引
int lastIndexOf(String str, int fromIndex):返回指定子字符串在此字符串中最后一次出现处的索引,从指定的索引开始反向搜索

注:indexOf和lastIndexOf方法如果未找到都是返回-1

替换:
String replace(char oldChar, char newChar):返回一个新的字符串,它是通过用 newChar 替换此字符串中出现的所 oldChar 得到的。
String replace(CharSequence target, CharSequence replacement):使用指定的字面值替换序列替换此字符串所匹配字面值目标序列的子字符串。
String replaceAll(String regex, String replacement):使用给定的 replacement 替换此字符串所匹配给定的正则表达式的子字符串。
String replaceFirst(String regex, String replacement):使用给定的 replacement 替换此字符串匹配给定的正则表达式的第一个子字符串。
匹配:
boolean matches(String regex):告知此字符串是否匹配给定的正则表达式。
切片:
String[] split(String regex):根据给定正则表达式的匹配拆分此字符串。
String[] split(String regex, int limit):根据匹配给定的正则表达式来拆分此字符串,最多不超过limit个,如果超过了,剩下的全部都放到最后一个元素中。

StringBuffer,StringBuilder类

1.String、StringBuffer、StringBuilder三者的对比
String:不可变的字符序列;底层使用char[]存储
StringBuffer:可变的字符序列;线程安全的,效率低;底层使用char[]存储
StringBuilder:可变的字符序列;jdk5.0新增的,线程不安全的,效率高;底层使用char[]存储

2.StringBuffer、StringBuilder中的常用方法

增:append(xxx)
删:delete(int start,int end)
改:setCharAt(int n ,char ch) / replace(int start, int end, String str)
查:charAt(int n )
插:insert(int offset, xxx)
长度:length();
*遍历:for() + charAt() / toString()

关于日期时间的API

比较器

Java中的对象,正常情况下,只能进行比较:== 或 != 。不能使用 > 或 < 的
但是在开发场景中,我们需要对多个对象进行排序,言外之意,就需要比较对象的大小。
如何实现?使用两个接口中的任何一个:Comparable 或 Comparator

自然排序:使用Comparable接口

  • 1.像String、包装类等实现了Comparable接口,重写了compareTo(obj)方法,给出了比较两个对象大小的方式。
  • 2.像String、包装类重写compareTo()方法以后,进行了从小到大的排列
  • 3.重写compareTo(obj)的规则:
    如果当前对象this大于形参对象obj,则返回正整数;
    如果当前对象this小于形参对象obj,则返回负整数, 如果当前对象this等于形参对象obj,则返回零。
  • 对于自定义类来说,如果需要排序,我们可以让自定义类实现Comparable接口,重写compareTo(obj)方法。在compareTo(obj)方法中指明如何排序

定制排序:使用Comparator接口

3.1 说明
1.背景:
当元素的类型没实现java.lang.Comparable接口而又不方便修改代码,或者实现了java.lang.Comparable接口的排序规则不适合当前的操作,那么可以考虑使用 Comparator 的对象来排序
2.重写compare(Object o1,Object o2)方法,比较o1和o2的大小:
如果方法返回正整数,则表示o1大于o2;
如果返回0,表示相等;
返回负整数,表示o1小于o2。

枚举类

枚举类的说明:

  • 1.枚举类的理解:类的对象只有有限个,确定的。我们称此类为枚举类
  • 2.当需要定义一组常量时,强烈建议使用枚举类
  • 3.如果枚举类中只一个对象,则可以作为单例模式的实现方式。

注解

注解的理解

① jdk 5.0 新增的功能
② Annotation 其实就是代码里的特殊标记, 这些标记可以在编译, 类加载, 运行时被读取, 并执行相应的处理。通过使用 Annotation,
程序员可以在不改变原逻辑的情况下, 在源文件中嵌入一些补充信息。
③在JavaSE中,注解的使用目的比较简单,例如标记过时的功能,忽略警告等。在JavaEE/Android
中注解占据了更重要的角色,例如用来配置应用程序的任何切面,代替JavaEE旧版中所遗留的繁冗代码和XML配置等。

集合

详情可见集合总结

IO流

流的分类

  • 1.操作数据单位:字节流、字符流
  • 2.数据的流向:输入流、输出流
  • 3.流的角色:节点流、处理流

输入过程
① 创建File类的对象,指明读取的数据的来源。(要求此文件一定要存在)
② 创建相应的输入流,将File类的对象作为参数,传入流的构造器中
③ 具体的读入过程:
创建相应的byte[] 或 char[]。
④ 关闭流资源
说明:程序中出现的异常需要使用try-catch-finally处理。

输出过程
① 创建File类的对象,指明写出的数据的位置。(不要求此文件一定要存在)
② 创建相应的输出流,将File类的对象作为参数,传入流的构造器中
③ 具体的写出过程:
write(char[]/byte[] buffer,0,len)
④ 关闭流资源
说明:程序中出现的异常需要使用try-catch-finally处理。

泛型

1.泛型的概念
所谓泛型,就是允许在定义类、接口时通过一个标识表示类中某个属性的类型或者是某个方法的返
回值及参数类型。这个类型参数将在使用时(例如,继承或实现这个接口,用这个类型声明变量、
创建对象时确定(即传入实际的类型参数,也称为类型实参)。

2.泛型的引入背景
集合容器类在设计阶段/声明阶段不能确定这个容器到底实际存的是什么类型的对象,所以在JDK1.5之前只能把元素类型设计为Object,JDK1.5之后使用泛型来解决。因为这个时候除了元素的类型不确定,其他的部分是确定的,例如关于这个元素如何保存,如何管理等是确定的,因此此时把元素的类型设计成一个参数,这个类型参数叫做泛型。Collection,List,ArrayList 这个就是类型参数,即泛型。

3.在集合中使用泛型的情况
在这里插入图片描述

网络编程

一、实现网络通信需要解决的两个问题

  • 1.如何准确地定位网络上一台或多台主机;定位主机上的特定的应用
  • 2.找到主机后如何可靠高效地进行数据传输

二、网络通信的两个要素:

  • 1.对应问题一:IP和端口号
  • 2.对应问题二:提供网络通信协议:TCP/IP参考模型(应用层、传输层、网络层、物理+数据链路层)

三、通信要素一:IP和端口号
1.IP的理解

  • 1.IP:唯一的标识 Internet 上的计算机(通信实体)
  • 2.在Java中使用InetAddress类代表IP
  • 3.IP分类:IPv4 和 IPv6 ; 万维网 和 局域网
  • 4.域名: www.baidu.com www.mi.com www.sina.com www.jd.com
  • 域名解析:域名容易记忆,当在连接网络时输入一个主机的域名后,域名服务器(DNS)负责将域名转化成IP地址,这样才能和主机建立连接。 -------域名解析
  • 5.本地回路地址:127.0.0.1 对应着:localhost

2.InetAddress类:此类的一个对象就代表着一个具体的IP地址

  • 2.1实例化 getByName(String host) 、 getLocalHost()
  • 2.2常用方法 getHostName() / getHostAddress()

3.端口号:正在计算机上运行的进程。

  • 要求:不同的进程不同的端口号
  • 范围:被规定为一个 16 位的整数 0~65535。

端口号与IP地址的组合得出一个网络套接字:Socket

4.TCP和UDP的区别
在这里插入图片描述

5.TCP三次握手和四次挥手

在这里插入图片描述
在这里插入图片描述

反射

获取Class类的实例

获取Class实例的几种方式:

	//方式一:调用运行时类的属性:.class
        Class clazz1 = Person.class;
        System.out.println(clazz1);
        //方式二:通过运行时类的对象,调用getClass()
        Person p1 = new Person();
        Class clazz2 = p1.getClass();
        System.out.println(clazz2);

        //方式三:调用Class的静态方法:forName(String classPath)
        Class clazz3 = Class.forName("com.atguigu.java.Person");
//        clazz3 = Class.forName("java.lang.String");
        System.out.println(clazz3);

        System.out.println(clazz1 == clazz2);
        System.out.println(clazz1 == clazz3);

反射的四大应用

应用一:创建运行时类的对象
代码举例

Class<Person> clazz = Person.class;

Person obj = clazz.newInstance();
System.out.println(obj);

说明
newInstance():调用此方法,创建对应的运行时类的对象。内部调用了运行时类的空参的构造器。

要想此方法正常的创建运行时类的对象,要求:
1.运行时类必须提供空参的构造器
2.空参的构造器的访问权限得够。通常,设置为public

应用二:获取运行时类的完整结构

我们可以通过反射,获取对应的运行时类中所有的属性、方法、构造器、父类、接口、父类的泛型、包、注解、异常等。

典型代码:

@Test
public void test1(){
    
    

    Class clazz = Person.class;

    //获取属性结构
    //getFields():获取当前运行时类及其父类中声明为public访问权限的属性
    Field[] fields = clazz.getFields();
    for(Field f : fields){
    
    
        System.out.println(f);
    }
    System.out.println();

    //getDeclaredFields():获取当前运行时类中声明的所属性。(不包含父类中声明的属性
    Field[] declaredFields = clazz.getDeclaredFields();
    for(Field f : declaredFields){
    
    
        System.out.println(f);
    }
}

@Test
public void test1(){
    
    

    Class clazz = Person.class;

    //getMethods():获取当前运行时类及其所父类中声明为public权限的方法
    Method[] methods = clazz.getMethods();
    for(Method m : methods){
    
    
        System.out.println(m);
    }
    System.out.println();
    //getDeclaredMethods():获取当前运行时类中声明的所方法。(不包含父类中声明的方法
    Method[] declaredMethods = clazz.getDeclaredMethods();
    for(Method m : declaredMethods){
    
    
        System.out.println(m);
    }
}

/*
    获取构造器结构

     */
    @Test
    public void test1(){
    
    

        Class clazz = Person.class;
        //getConstructors():获取当前运行时类中声明为public的构造器
        Constructor[] constructors = clazz.getConstructors();
        for(Constructor c : constructors){
    
    
            System.out.println(c);
        }

        System.out.println();
        //getDeclaredConstructors():获取当前运行时类中声明的所的构造器
        Constructor[] declaredConstructors = clazz.getDeclaredConstructors();
        for(Constructor c : declaredConstructors){
    
    
            System.out.println(c);
        }

    }

    /*
    获取运行时类的父类

     */
    @Test
    public void test2(){
    
    
        Class clazz = Person.class;

        Class superclass = clazz.getSuperclass();
        System.out.println(superclass);
    }

    /*
    获取运行时类的带泛型的父类

     */
    @Test
    public void test3(){
    
    
        Class clazz = Person.class;

        Type genericSuperclass = clazz.getGenericSuperclass();
        System.out.println(genericSuperclass);
    }

    /*
    获取运行时类的带泛型的父类的泛型

    代码:逻辑性代码  vs 功能性代码
     */
    @Test
    public void test4(){
    
    
        Class clazz = Person.class;

        Type genericSuperclass = clazz.getGenericSuperclass();
        ParameterizedType paramType = (ParameterizedType) genericSuperclass;
        //获取泛型类型
        Type[] actualTypeArguments = paramType.getActualTypeArguments();
//        System.out.println(actualTypeArguments[0].getTypeName());
        System.out.println(((Class)actualTypeArguments[0]).getName());
    }

    /*
    获取运行时类实现的接口
     */
    @Test
    public void test5(){
    
    
        Class clazz = Person.class;

        Class[] interfaces = clazz.getInterfaces();
        for(Class c : interfaces){
    
    
            System.out.println(c);
        }

        System.out.println();
        //获取运行时类的父类实现的接口
        Class[] interfaces1 = clazz.getSuperclass().getInterfaces();
        for(Class c : interfaces1){
    
    
            System.out.println(c);
        }

    }
    /*
        获取运行时类所在的包

     */
    @Test
    public void test6(){
    
    
        Class clazz = Person.class;

        Package pack = clazz.getPackage();
        System.out.println(pack);
    }

    /*
        获取运行时类声明的注解

     */
    @Test
    public void test7(){
    
    
        Class clazz = Person.class;

        Annotation[] annotations = clazz.getAnnotations();
        for(Annotation annos : annotations){
    
    
            System.out.println(annos);
        }
    }

应用三:调用运行时类的完整结构

调用指定的属性:
@Test
public void testField1() throws Exception {
    
    
    Class clazz = Person.class;

    //创建运行时类的对象
    Person p = (Person) clazz.newInstance();

    //1. getDeclaredField(String fieldName):获取运行时类中指定变量名的属性
    Field name = clazz.getDeclaredField("name");

    //2.保证当前属性是可访问的
    name.setAccessible(true);
    //3.获取、设置指定对象的此属性值
    name.set(p,"Tom");

    System.out.println(name.get(p));
}
调用指定的方法:
 @Test
    public void testMethod() throws Exception {
    
    

        Class clazz = Person.class;

        //创建运行时类的对象
        Person p = (Person) clazz.newInstance();

        /*
        1.获取指定的某个方法
        getDeclaredMethod():参数1 :指明获取的方法的名称  参数2:指明获取的方法的形参列表
         */
        Method show = clazz.getDeclaredMethod("show", String.class);
        //2.保证当前方法是可访问的
        show.setAccessible(true);

        /*
        3. 调用方法的invoke():参数1:方法的调用者  参数2:给方法形参赋值的实参
        invoke()的返回值即为对应类中调用的方法的返回值。
         */
        Object returnValue = show.invoke(p,"CHN"); //String nation = p.show("CHN");
        System.out.println(returnValue);

        System.out.println("*************如何调用静态方法*****************");

        // private static void showDesc()

        Method showDesc = clazz.getDeclaredMethod("showDesc");
        showDesc.setAccessible(true);
        //如果调用的运行时类中的方法没返回值,则此invoke()返回null
//        Object returnVal = showDesc.invoke(null);   因为静态方法是跟着类的,类其实在获得方法时已经指明了,所以调用者其实已经确定了,所以不用指明调用者自然也是可以的
        Object returnVal = showDesc.invoke(Person.class);
        System.out.println(returnVal);//null

    }

调用指定的构造器:
@Test
public void testConstructor() throws Exception {
    
    
    Class clazz = Person.class;

    //private Person(String name)
    /*
    1.获取指定的构造器
    getDeclaredConstructor():参数:指明构造器的参数列表
     */

    Constructor constructor = clazz.getDeclaredConstructor(String.class);

    //2.保证此构造器是可访问的
    constructor.setAccessible(true);

    //3.调用此构造器创建运行时类的对象
    Person per = (Person) constructor.newInstance("Tom");
    System.out.println(per);

}

应用四:动态代理

代理模式的原理:

使用一个代理将对象包装起来, 然后用该代理对象取代原始对象。任何对原始对象的调用都要通过代理。代理对象决定是否以及何时将方法调用转到原始对象上。

动态代理的实现

需要解决的两个主要问题:

问题一:如何根据加载到内存中的被代理类,动态的创建一个代理类及其对象。
(通过Proxy.newProxyInstance()实现)
问题二:当通过代理类的对象调用方法a时,如何动态的去调用被代理类中的同名方法a。
(通过InvocationHandler接口的实现类及其方法invoke())

代码实现:

interface Human{
    
    

    String getBelief();

    void eat(String food);

}
//被代理类
class SuperMan implements Human{
    
    


    @Override
    public String getBelief() {
    
    
        return "I believe I can fly!";
    }

    @Override
    public void eat(String food) {
    
    
        System.out.println("我喜欢吃" + food);
    }
}

class HumanUtil{
    
    

    public void method1(){
    
    
        System.out.println("====================通用方法一====================");

    }

    public void method2(){
    
    
        System.out.println("====================通用方法二====================");
    }

}


class ProxyFactory{
    
    
    //调用此方法,返回一个代理类的对象。解决问题一
    public static Object getProxyInstance(Object obj){
    
    //obj:被代理类的对象
        MyInvocationHandler handler = new MyInvocationHandler();

        handler.bind(obj);

        return Proxy.newProxyInstance(obj.getClass().getClassLoader(),obj.getClass().getInterfaces(),handler);
    }

}

class MyInvocationHandler implements InvocationHandler{
    
    

    private Object obj;//需要使用被代理类的对象进行赋值

    public void bind(Object obj){
    
    
        this.obj = obj;
    }

    //当我们通过代理类的对象,调用方法a时,就会自动的调用如下的方法:invoke()
    //将被代理类要执行的方法a的功能就声明在invoke()中
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    
    

        HumanUtil util = new HumanUtil();
        util.method1();

        //method:即为代理类对象调用的方法,此方法也就作为了被代理类对象要调用的方法
        //obj:被代理类的对象
        Object returnValue = method.invoke(obj,args);

        util.method2();

        //上述方法的返回值就作为当前类中的invoke()的返回值。
        return returnValue;

    }
}

public class ProxyTest {
    
    

    public static void main(String[] args) {
    
    
        SuperMan superMan = new SuperMan();
        //proxyInstance:代理类的对象
        Human proxyInstance = (Human) ProxyFactory.getProxyInstance(superMan);
        //当通过代理类对象调用方法时,会自动的调用被代理类中同名的方法
        String belief = proxyInstance.getBelief();
        System.out.println(belief);
        proxyInstance.eat("四川麻辣烫");

        System.out.println("*****************************");

        NikeClothFactory nikeClothFactory = new NikeClothFactory();

        ClothFactory proxyClothFactory = (ClothFactory) ProxyFactory.getProxyInstance(nikeClothFactory);

        proxyClothFactory.produceCloth();

    }
}

思路:

我们设置ProxyFactory为生产动态代理类的工厂,其设置一个传入的参数为要代理的对象,工厂内部设置一个静态方法,根据代理的对象及其一些属性来生成代理类的对象,这里直接使用newProxyInstance方法封装了。即这个方法内部自己给我们造出对象了,只要我们给他足够的细节即可。
但是有一个接口的实现类需要我们提供,这个玩意是为了实现我们“调用代理类的对象的方法,其实本质是调用被代理类的对象方法”。
通过它,我们可以实现当我们通过代理类的对象,调用方法a时,就会自动的调用如下的方法:invoke()
在这个invoke方法内部,来完成我们想调用代理类对象方法的具体实现,由于给了许多参数,直接根据参数通过反射思想调用反射中的invoke方法即可。

这种动态代理是怎么建成的呢?
主要是中间部分是抽象的,可以根据你输入的被代理类的类型来确定执行什么东西,因此相当于无形中创建了好多的代理类。

java8新特性

Stream流

详情可见Stream总结

猜你喜欢

转载自blog.csdn.net/xiangguang_fight/article/details/119075912