封装、继承、多态、抽象。
抽象
封装
继承
多态
用new来创建对象实例(对象实例在堆内存中),对象引用指向对象实例(对象引用存放在栈内存中)。
一个对象引用可以指向 0 个或 1 个对象(一根绳子可以不系气球,也可以系一个气球);一个对象可以有 n 个引用指向它(可以用 n 条绳子系住一个气球)。
主要作用是完成对类对象的初始化工作。可以执行。因为一个类即使没有声明构造方法也会有默认的不带参数的构造方法。
帮助子类完成初始化工作。
public static void main(String[] args) {
int num1 = 10;
int num2 = 20;
swap(num1, num2);
System.out.println("num1 = " + num1);//10
System.out.println("num2 = " + num2);//20
}
public static void swap(int a, int b) {
int temp = a;
a = b;
b = temp;
}
public static void main(String[] args) {
int[] arr = {
1, 2, 3, 4, 5 };
System.out.println(arr[0]);//1
change(arr);
System.out.println(arr[0]);//0
}
public static void change(int[] array) {
// 将数组的第一个元素变为0
array[0] = 0;
}
千万不要以为传递的参数是值就是值传递,传递的是引用就是引用传递。也不要以为传递的参数是基本数据类型就是值传递,传递的是对象就是引用传递。
值传递(pass by value)是指在调用函数时将实际参数复制一份传递到函数中,这样在函数中如果对参数进行修改,将不会影响到实际参数。
引用传递(pass by reference)是指在调用函数时将实际参数的地址直接传递到函数中,那么在函数中对参数所进行的修改,将影响到实际参数。
所以,值传递和引用传递的区别并不是传递的内容。而是实参到底有没有被复制一份给形参。
值传递(pass by value)是指在调用函数时将实际参数复制一份传递到函数中,这样在函数中如果对参数进行修改,将不会影响到实际参数。
引用传递(pass by reference)是指在调用函数时将实际参数的地址直接传递到函数中,那么在函数中对参数所进行的修改,将影响到实际参数。
Java 程序设计语言总是采用按值调用。不论传递的参数类型是值类型还是引用类型,都会在调用栈上创建一个副本。不同的是,对于值类型来说,复制的就是整个原始值的复制。而对于引用类型来说,由于在调用栈中只存储对象的引用,因此复制的只是这个引用,而不是原始对象。
使用 transient 关键字修饰。
transient 关键字的作用是:阻止实例中那些用此关键字修饰的的变量序列化;当对象被反序列化时,被 transient 修饰的变量值不会被持久化和恢复。transient 只能修饰变量,不能修饰类和方法
方法一:通过 Scanner
Scanner input = new Scanner(System.in);
String s = input.nextLine();
input.close();
方法二:通过BufferedReader
BufferedReader input = new BufferedReader(new InputStreamReader(System.in));
String s = input.readLine();
一个程序中可以有多个类,但只能有一个类是主类。在Java应用程序中,这个主类是指包含main()方法的类。
而在Java小程序中,这个主类是一个继承自系统类JApplet或Applet的子类。
应用程序的主类不一定要求是public类,但小程序的主类要求必须是public类。主类是Java程序执行的入口点。
private只有当前类下可以访问,default是同包下的就可以访问,protected是子类也可以访问
(子类和父类不再同一个包中,所以你看看子类连default都访问不了,而同包中的类还可以访问)
①类的成员不写访问修饰时默认为default。
默认(default)对于同一个包中的其他类相当于公开(public),对于不是同一个包中的其他类相当于私有(private)。
②受保护(protected)对子类相当于公开,对不是同一包中的没有父子关系的类相当于私有。
③Java中,外部类的修饰符只能是public或默认,类的成员(包括内部类)的修饰符可以是以上四种。
数据类型转换参考:Day01_入门程序、常量、变量
不正确。3.4是双精度数,将双精度型(double)赋值给浮点型(float)属于下转型会造成精度损失,因此需要强制类型转换float f =(float)3.4; 或者写成float f =3.4F;。
goto 是Java中的保留字,在目前版本的Java中没有使用。(Java关键字列表中有goto和const,但是这两个是目前无法使用的关键字,因此有些地方将其称之为保留字,在系统类库中使用过的有特殊意义的单词或单词的组合都被视为保留字)
&运算符有两种用法:(1)按位与;(2)逻辑与。
&&运算符是短路与运算。
逻辑与跟短路与的差别是非常巨大的,虽然二者都要求运算符左右两端的布尔值都是true整个表达式的值才是true。
&&之所以称为短路运算是因为,如果&&左边的表达式的值是false,右边的表达式会被直接短路掉,不会进行运算。很多时候我们可能都需要用&&而不是&,例如在验证用户登录时判定用户名不是null而且不是空字符串,应当写为:username != null &&!username.equals(""),二者的顺序不能交换,更不能用&运算符,因为第一个条件如果不成立,根本不能进行字符串的equals比较,否则会产生NullPointerException异常。注意:逻辑或运算符(|)和短路或运算符(||)的差别也是如此。
Math.round(11.5)的返回值是12,Math.round(-11.5)的返回值是-11。四舍五入的原理是在参数上加0.5然后进行下取整。
2 << 3,
因为将一个数左移n 位,就相当于乘以了2 的n 次方,那么,一个数乘以8 只要将其左移3 位
即可,而位运算cpu 直接支持的,效率最高,所以,2 乘以8 等於几的最效率的方法是2 << 3。
定义:用于解释说明程序的文字
Java注释的作用:
在程序中,尤其是复杂的程序中,适当地加入注释可以增加程序的可读性,有利于程序的修改、调试和交流。注释的内容在程序编译的时候会被忽视,不会产生目标代码,注释的部分不会对程序的执行结果产生任何影响。
基本数据类型sout打印出来的是数值, 引用数据类型sout打印出来的是对象的地址值
其中八种基本数据类型:byte、short、char‐‐>int‐‐>long‐‐>float‐‐>double,boolean
用于修饰类、属性和方法;
被final修饰的类不可以被继承
被final修饰的方法不可以被重写
被final修饰的变量不可以被改变,被final修饰不可变的是变量的引用,而不是引用指向的内容,引用指向的内容是可以改变的
不一样,因为内存的分配方式不一样。String str = "i"的方式JVM会将其分配到常量池中,而 String str = new String(“i”)JVM会将其分配到堆内存中。
学习视频:第300集
操作字符串的类有:String、StringBuffer、StringBuilder。
String 和 StringBuffer、StringBuilder 的区别在于 String 声明的是不可变的对象,每次操作都会生成新的 String 对象,再将指针指向新的 String 对象,而 StringBuffer 、 StringBuilder 可以在原有对象的基础上进行操作,所以在经常改变字符串内容的情况下最好不要使用 String。
buffer听起来像“并发”
StringBuffer 和 StringBuilder 最大的区别在于,StringBuffer 是线程安全的,而 StringBuilder 是非线程安全的,但 StringBuilder 的性能却高于 StringBuffer,所以在单线程环境下推荐使用 StringBuilder,多线程环境下推荐使用 StringBuffer。
在实际编程过程中,我们常常要遇到这种情况:有一个对象 A,在某一时刻 A 中已经包含了一些有效值,此时可能会需要一个和 A 完全相同新对象 B,并且此后对 B 任何改动都不会影响到 A 中的值,也就是说,A 与 B 是两个独立的对象,但 B 的初始值是由 A 对象确定的。在 Java 语言中,用简单的赋值语句是不能满足这种需求的。要满足这种需求虽然有很多途径,但clone()方法是其中最简单,也是最高效的手段。
浅克隆:创建一个新对象,新对象的属性和原来对象完全相同,对于引用数据属性,仍指向原有属性所指向的对象的内存地址。
深克隆:创建一个新对象,属性中引用的其他对象也会被克隆,不再指向原有对象地址。
new 操作符的本意是分配内存。程序执行到 new 操作符时,首先去看 new 操作符后面的类型,因为知道了类型,才能知道要分配多大的内存空间。分配完内存之后,再调用构造函数,填充对象的各个域,这一步叫做对象的初始化,构造方法返回后,一个对象创建完毕,可以把他的引用(地址)发布到外部,在外部就可以使用这个引用操纵这个对象。
clone 在第一步是和 new 相似的,都是分配内存,调用 clone 方法时,分配的内存和原对象(即调用 clone 方法的对象)相同,然后再使用原对象中对应的各个域,填充新对象的域,填充完成之后,clone方法返回,一个新的相同的对象被创建,同样可以把这个新对象的引用发布到外部。
笔记链接:Day07_多态、final关键字、权限修饰符、内部类
接口
定义的引用变量
可以指向具体实现类
的实现方法
,该引用变量发出的方法调用到底是哪个类中实现的方法
,必须在程序运行期
间才能决定。因为在程序运行时才确定具体的类,这样,不用修改源代码,就可以让引用变量绑定到各种不同的对象上
,从而导致该引用调用的具体方法随之改变,让程序可以选择多个运行状态,这就是多态性。
(大白话:Fu类接口定义了方法eat(),Zi类定义了实现方法eat(),Fu obj = new Zi();
那么obj.eat()的输出就是子类的eat()方法的输出,不用修改父类的eat()方法就可以输出新的内容)
接口
定义的引用变量
可以指向具体实现类
的实现方法
,该引用变量发出的方法调用到底是哪个类中实现的方法
,必须在程序运行期
间才能决定。因为在程序运行时才确定具体的类,这样,不用修改源代码,就可以让引用变量绑定到各种不同的对象上
,从而导致该引用调用的具体方法随之改变,让程序可以选择多个运行状态,这就是多态性。
(大白话:Fu类接口定义了方法eat(),Zi类定义了实现方法eat(),Fu obj = new Zi();
那么obj.eat()的输出就是子类的eat()方法的输出,不用修改父类的eat()方法就可以输出新的内容)
答:构造器不能被继承,因此不能被重写,但可以被重载。
String 类是final类,不可以被继承。
补充:继承String本身就是一个错误的行为,对String类型最好的重用方式是关联关系(Has-A)和依赖关系(Use-A)而不是继承关系(Is-A)。
•splt();字符串分割,返回分割后的字符串数组。
•getBytes();返回字符串byte类型数组。
•length();返回字符串长度。
•toLowerCase();将字符串转换为小写字母。
•toUpperCase();将字符串转换为大写字母。
•substring();字符串截取。
•equals();比较字符串是否相等。
•replace();字符串替换。
•indexof();返回指定字符的的索引。
•charAt();返回指定索引处的字符。
•trim();去除字符串两端空格。
char可以存储一个中文汉字,因为Java中使用的编码是Unicode,一个char 类型占2个字节(16 比特),所以放一个中文是没问题的。
位 = 比特 = bit :信息的最小单位 ,等于0或1;
字节 = byte:可表示一个英文字母、一个数字或一个符号,不能表示一个汉字;
一个字节等于八比特,1 byte =8 bit
1Byte=8bit
1 KB = 1,024 Bytes
1 MB = 1,024 KB= 1024^2 Bytes
1 GB = 1,024 MB= 1024^2 KB= 1024^3 Bytes
GBK编码,一个汉字占两个字节。UTF-8编码,通常汉字占三个字节。
编码不同,汉字所占的字节也不同;通常一个汉字是2-4个字节。
一个char 类型占2个字节(16 比特),那么一个char是否能表示一个中文?
可以,完全可以,因为Java中采用的不是GBK、UTF-8编码,而是采用的Unicode编码,
Unicode是用16位表示的,char也是16位的,Unicode字符集有包含中文,所以放一个中文完全没有问题。
笔记链接:Day05_关键字【Static、this、super】
1. 在本类的成员方法中,访问本类的成员变量。
public class Zi extends Fu {
int num = 20;
public void method() {
int num = 30;
System.out.println(num); // 30,局部变量
System.out.println(this.num); // 20,本类的成员变量
System.out.println(super.num); // 10,父类的成员变量
}
}
2. 在本类的成员方法中,访问本类的另一个成员方法。
public class Zi extends Fu {
public void methodA() {
System.out.println("AAA");
}
public void methodB() {
this.methodA(); //在本类的成员方法中,访问本类的另一个成员方法。
System.out.println("BBB");
}
}
3. 在本类的构造方法中,访问本类的另一个构造方法。
public class Zi extends Fu {
public Zi() {
this(123); // 本类的无参构造,调用本类的有参构造
}
public Zi(int n) {
this(1, 2); //在本类的构造方法中,访问本类的另一个构造方法。
}
public Zi(int n, int m) {
}
}
笔记链接:Day05_关键字【Static、this、super】
public class Fu {
public Fu() {
System.out.println("父类的无参数构造方法!");
}
}
public class Zi extends Fu {
public Zi() {
super(); // 不管你写不写这行代码,系统都默认给你带上了
System.out.println("子类构造方法!");
}
}
public class Demo01Constructor {
public static void main(String[] args) {
Zi zi = new Zi();
}
}
输出:
父类的无参数构造方法!
子类构造方法!
①创建独立于具体对象的域变量或者方法。 以致于即使没有创建对象,也能使用属性和调用方法 !
②static块可以用来优化程序性能,因为:只会在类加载的时候执行一次。因此,很多时候会将一些只需要进行一次的初始化操作都放在static代码块中进行。
独立于对象 且 优先于对象:
被static修饰的就是被整个类所共享的
1、静态只能访问静态。 2、非静态既可以访问非静态的,也可以访问静态的。
break 结束当前的循环体
continue 跳出本次循环,继续执行下次循环
return 程序返回,不再执行下面的代码(结束当前的方法 直接返回)
Java程序在执行子类的构造方法之前,如果没有用super()来调用父类特定的构造方法,则会调用父类中“没有参数的构造方法”。因此,如果父类中只定义了有参数的构造方法,而在子类的构造方法中又没有用super()来调用父类中特定的构造方法,则编译时将发生错误,因为Java程序在父类中找不到没有参数的构造方法可供执行。解决办法是在父类里加上一个不做事且没有参数的构造方法。
名字与类名相同;
没有返回值,但不能用void声明构造函数;
生成类的对象时自动执行,无需调用。
静态变量: 静态变量由于不属于任何实例对象,属于类的,所以在内存中只会有一份,在类的加载过程中,JVM只为静态变量分配一次内存空间。
实例变量: 每次创建对象,都会为每个对象分配成员变量内存空间,实例变量是属于实例对象的,在内存中,创建几次对象,就有几份成员变量。
静态方法和实例方法的区别主要体现在两个方面:
1.在外部调用静态方法时,可以使用"类名.方法名"的方式,也可以使用"对象名.方法名"的方式。而实例方法只有后面这种方式。也就是说,调用静态方法可以无需创建对象。
2.静态方法在访问本类的成员时,只允许访问静态成员(即静态成员变量和静态方法),而不允许访问实例成员变量和实例方法;实例方法则无此限制
方法的返回值是指我们获取到的某个方法体中的代码执行后产生的结果!(前提是该方法可能产生结果)。
返回值的作用:接收出结果,使得它可以用于其他的操作!
在Java中,可以将一个类的定义放在另外一个类的定义内部,这就是 内部类 。内部类本身就是类的一个属性,与其他属性定义方式一致。
内部类可以分为四种: 成员内部类、局部内部类、匿名内部类和静态内部类 。
按照异常需要处理的时机分为编译时异常 和运行时异常。
(1)编译时异常Java程序必须显式处理,如果程序没有处理就会发生错误无法编译。对编译时异常处理方法有两种:
●第一种:当前方法知道如何处理该异常,则用try…catch块来处理该异常。
●第二种:当前方法不知道如何处理,则在定义该方法时声明抛出该异常。(涉及到throw和throws的区别)
(2)运行时异常只有当代码在运行时才发行的异常,编译的时候不需要try…catch。
throw和throws作为Java中两种异常抛出关键字,虽然两个长的很像,但是却有着很大的区别。
throws:
跟在方法声明后面,后面跟的是异常类名
throw:
用在方法体内,后面跟的是异常类对象名
public static void method() throws ArithmeticException {
// 跟在方法声明后面,后面跟的是异常类名
int a=10;
int b=0;
if(b==0) {
throw new ArithmeticException();用在方法体内,后面跟的是异常类对象名
}else {
System.out.println(a/b);
}
}
}
throws:
可以跟多个异常类名,用逗号隔开
throw:
只能抛出一个异常对象名
public static void method() throws ArithmeticException,Exception {
//跟多个异常类名,用逗号隔开
int a=10;
int b=0;
if(b==0) {
throw new ArithmeticException();// 只能抛出一个异常对象名
}else {
System.out.println(a/b);
}
}
}
throws:
表示抛出异常,由该方法的调用者来处理
throw:
表示抛出异常,由该方法体内的语句来处理
public class throwandthrows {
public static void main(String[] args) {
try {
method();//由该方法的调用者来处理
}catch (ArithmeticException e) {
e.printStackTrace();
}
}
public static void method() throws ArithmeticException {
int a=10;
int b=0;
if(b==0) {
throw new ArithmeticException();//由该方法体内的语句来处理
}else {
System.out.println(a/b);
}
}
}
throws:
throws表示有出现异常的可能性,并不一定出现这些异常
throw:
throw则是抛出了异常,执行throw一定出现了某种异常
我们向上面例子代码里throws一个IndexOutOfBoundsException异常,编译发现并没有报错,这就体现了throws表示有出现异常的可能性
public class throwandthrows {
public static void main(String[] args) {
try {
method();
}catch (ArithmeticException e) {
e.printStackTrace();
}
}
public static void method() throws ArithmeticException,IndexOutOfBoundsException {
int a=10;
int b=0;
if(b==0) {
throw new ArithmeticException();
}else {
System.out.println(a/b);
}
}
}
学习文档:Day06_重写和重载、抽象类、接口
1.抽象类里面可以有抽象方法也可以存在其它非抽象方法,但是接口里面只能存在抽象方法(也就是被public abstract修饰的)
(ps:在jdk8和jdk9以后接口里面才有了默认方法、静态方法、私有方法)
2.抽象类中的成员变量可以是各种类型的,而接口中只能有常量(也就是要被public static final修饰的 );
3.一个类只能继承一个抽象类,而一个类却可以实现多个接口。
4…最后就是语义上的区别:什么时候用抽象类?抽象类描述的往往是我们平时能够接触到的概念,比如“动物”、“植物”、“食物”;
什么时候用接口?接口描述的某些共同事务之间所具有的共同特征,比如“会飞的”(蜻蜓会飞、奥特曼也会飞)
jdk8: 增加了default方法和static方法,这2种方法可以有方法体。
jdk9:接口允许 以 private 修饰的方法
1.关于抽象类的温习:
子类必须覆盖重写抽象父类当中所有的抽象方法。
一个抽象类不一定含有抽象方法,只要保证抽象方法所在的类是抽象类,即可。
public abstract class Animal {
// 这是一个抽象方法,代表吃东西,但是具体吃什么(大括号的内容)不确定。
public abstract void eat();
// 这是普通的成员方法
public void normalMethod() {
System.out.println("我不是抽象方法但我可以在抽象类中");
}
}
总之:抽象类可以有抽象方法,也可以有普通方法;子类继承了抽象的父类,那么子类就必须把父类里面的抽象方法都重写一遍(父类中的普通方法就可以不重写)
2.关于接口的温习:
接口的名字是MyInterAbstract,那么实现了这个接口的类名应该是MyInterAbstractImpl(即“接口名+Impl”)。
接口内容可以写什么呢?
重写(Override):方法的名称一样,参数列表【也一样】,返回值类型不能改变。
重载(Overload):方法的名称一样,参数列表【不一样】,返回值类型可以改变。
重写就是覆盖重写,方法名、参数、返回值类型都没有变,外壳不变,核心重写。
每个重载的方法都必须有一个独一无二的参数类型列表。
重载在同一个类中发生,重写在子类父类中发生。
代理可以对一些功能进行增强,比如增强参数列表、增强返回值类型、增强方法体执行逻辑
Proxy代理模式主要解决的问题是:在直接访问对象时带来的问题,为其他对象提供一个代理以控制对某个对象的访问。代理类负责为委托类预处理消息,过滤消息并转发消息,以及进行消息被委托类执行后的后续处理。
①静态代理中代理类在编译期就已经确定,而动态代理则是JVM运行时运用反射机制动态创建而成。
②静态代理的效率相对动态代理来说相对高一些,但是静态代理代码冗余大,一单需要修改接口,代理类和委托类都需要修改。
③静态代理只服务于一种类型的对象,如果要服务多类型的对象,势必要为每一种对象都进行代理,这样程序开发中必然会产生许多的代理类。对于动态代理,原接口中声明的所有方法都被转移到调用处理器一个集中的方法中处理(InvocationHandler.invoke)。这样,在接口方法数量比较多的时候,我们可以进行灵活处理。
1.静态代理
2.动态代理
Spring AOP使用动态代理技术在运行期织入增强代码。使用两种代理机制:基于JDK的动态代理(JDK本身只提供接口的代理)和基于CGlib的动态代理。
(1)JDK的动态代理
其代理对象必须是某个接口的实现, 它是通过在运行期间创建一个接口的实现类来完成对目标对象的代理.只能对实现接口的类生成代理,而不能针对类。
(2)CGLib
CGLib采用底层的字节码技术,为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类的调用方法,并顺势织入横切逻辑.它运行期间生成的代理对象是目标类的扩展子类.所以无法通知final、private的方法,因为它们不能被覆写.是针对类实现代理,主要是为指定的类生成一个子类,覆盖其中方法。
(1)当Bean实现接口时,Spring就会用JDK的动态代理
(2)当Bean没有实现接口时,Spring使用CGlib是实现
(3)可以强制使用CGlib(在spring配置中加入<aop:aspectj-autoproxy proxy-target-class=“true”/>)
实现AOP的技术,主要分为两大类:
一是采用动态代理技术,利用截取消息的方式,对该消息进行装饰,以取代原有对象行为的执行;
二是采用静态织入的方式,引入特定的语法创建“方面”,从而使得编译器可以在编译期间织入有关“方面”的代码。
Spring AOP使用动态代理技术在运行期织入增强代码。使用两种代理机制:基于JDK的动态代理(JDK本身只提供接口的代理)和基于CGlib的动态代理。
(1)JDK的动态代理
其代理对象必须是某个接口的实现, 它是通过在运行期间创建一个接口的实现类来完成对目标对象的代理.只能对实现接口的类生成代理,而不能针对类。
(2)CGLib
CGLib采用底层的字节码技术,为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类的调用方法,并顺势织入横切逻辑.它运行期间生成的代理对象是目标类的扩展子类.所以无法通知final、private的方法,因为它们不能被覆写.是针对类实现代理,主要是为指定的类生成一个子类,覆盖其中方法。
1、为什么需要自定义异常类
一些异常都是 Java 内部定义好的,但是实际开发中也会出现很多异常,这些异常很可能在JDK中没有定义过,例如年龄负数问题,考试成绩负数问题,这时就需要我们自定义异常。在开发中根据自己业务的异常情况来定义异常类就是自定义异常。
2、异常类如何定义
(1)自定义一个编译器异常:自定义类 并继承 java.lang.Exception
(2)自定义一个运行时期的异常类:自定义类,并继承于 java.lang.RuntimeException。
建议:保留两种构造器的形式
①无参构造
②带给父类的message属性赋值的构造器
注意:
自定义异常类,必须的继承Exception或者RuntimeException
继承Exception:那么自定义的异常类就是一个编译期异常,如果方法内部抛出了编译期异常,就必须处理这个异常,要么throws,要么try…catch;
继承RuntimeException:那么自定义的异常类就是一个运行期异常,无需处理,交给虚拟机处理(中断处理)
示例代码:
@Setter
@Getter
@ToString
public class Student {
private int id;
private String name;
private String age;
public Student(int id, String name, String age) {
this.id = id;
this.name = name;
this.age = age;
}
}
/**
* 自定义异常类
*/
public class StudentExcePtion extends Exception{
public StudentExcePtion(String messagee) {
super(messagee);
}
}
public class Test implements StudentManager{
static int total = 2; //总人数
static int count = 0; //当前人数
Student[] st = new Student[total];
@Override
public void add(Student stu) throws StudentExcePtion {
if(count<total){
st[count] = stu;
count++;
}else {
throw new StudentExcePtion("越界了");
}
}
public static void main(String[] args) throws StudentExcePtion {
Test sm = new Test();
Student s1 = new Student(01, "tom1", "21");
Student s2 = new Student(02, "tom2", "22");
Student s3 = new Student(03, "tom3", "23");
sm.add(s1);
sm.add(s2);
sm.add(s3);
}
}
如果一个类被final修饰,此类不可以有子类,不能被其它类继承,如果一个中的所有方法都没有重写的需要,当前类没有子类也罢,就可以使用final修饰类。
以Math类为例:
虽然目前处理器核心数已经发展到很大数目,但是按任务并发处理并不能完全充分的利用处理器资源,因为一般的应用程序没有那么多的并发处理任务。基于这种现状,考虑把一个任务拆分成多个单元,每个单元分别得到执行,最后合并每个单元的结果。
Fork/Join框架是一个用于并行执行任务的框架,是一个把大任务分割成若干小任务,最终汇总每个小任务结果得到大任务结果的框架。
线程池中的每个线程都有自己的工作队列(PS,这一点和ThreadPoolExecutor不同,ThreadPoolExecutor是所有线程公用一个工作队列,所有线程都从这个工作队列中取任务),当自己队列中的任务都完成以后,会从其它线程的工作队列中偷一个任务执行,这样可以充分利用资源。
A线程处理完了自己队列的任务,B线程的队列里还有很多任务要处理。 A想过去帮忙,但是如果两个线程访问同一个队列,会产生竞争,所以A想了一个办法,从双端队列的尾部拿任务执行。而B线程永远是从双端队列的头部拿任务执行。
1.通过 ForkJoinPool 来执行
2.计算任务 execute(ForkJoinTask<?> task)
3.计算类要去继承 ForkJoinTask;
1.写一个ForkJoin类
public class ForkJoinDemo extends RecursiveTask<Long> {
private Long start;
private Long end;
// 临界值(start-end小于它那就使用普通的计算方式,如果start-end大于临界值就是用ForkJoin的方式)
private Long temp = 10000L;
public ForkJoinDemo(Long start, Long end) {
this.start = start;
this.end = end;
}
// 计算方法
@Override
protected Long compute() {
if ((end-start)<temp){
Long sum = 0L;
for (Long i = start; i <= end; i++) {
sum += i;
}
return sum;
}else {
// forkjoin 递归
long middle = (start + end) / 2; // 中间值
ForkJoinDemo task1 = new ForkJoinDemo(start, middle);// 拆分任务
task1.fork(); // 把任务压入线程队列
ForkJoinDemo task2 = new ForkJoinDemo(middle+1, end);// 拆分任务
task2.fork(); // 把任务压入线程队列
return task1.join() + task2.join();
}
}
}
2.测试类
public class Test {
public static void main(String[] args) throws ExecutionException, InterruptedException {
test3();
}
// 普通程序员(用for循环完成1到10亿的累和)
public static void test1(){
Long sum = 0L;
long start = System.currentTimeMillis();//开始时间
for (Long i = 1L; i <= 10_0000_0000; i++) {
sum += i;
}
long end = System.currentTimeMillis();//结束时间
System.out.println("sum="+sum+" 时间:"+(end-start));//用时6105
}
// 会使用ForkJoin
public static void test2() throws ExecutionException, InterruptedException {
long start = System.currentTimeMillis();
ForkJoinPool forkJoinPool = new ForkJoinPool();
ForkJoinTask<Long> task = new ForkJoinDemo(0L, 10_0000_0000L);
ForkJoinTask<Long> submit = forkJoinPool.submit(task);// 提交任务
Long sum = submit.get();//获取到任务的返回值
long end = System.currentTimeMillis();
System.out.println("sum="+sum+" 时间:"+(end-start));//用时3876
}
//用Stream并行流 (),效率会高更多
public static void test3(){
long start = System.currentTimeMillis();
long sum = LongStream.rangeClosed(0L, 10_0000_0000L).parallel().reduce(0, Long::sum);//用时197
long end = System.currentTimeMillis();
System.out.println("sum="+"时间:"+(end-start));
}
}
增加了Lambda 表达式
强大的 Stream API(集合数据的操作)
最大化的减少空指针 异常:Optional 类 的使用
接口的新特性
注解的新特性
集合的底层 源码实现
新日期时间的 api
属性 | 含义 |
---|---|
Instant | 代表的是时间戳 |
LocalDate | 代表日期,比如2020-01-14 |
LocalTime | 代表时刻,比如12:59:59 |
LocalDateTime | 代表具体时间 2020-01-12 12:22:26 |
ZonedDateTime | 代表一个包含时区的完整的日期时间,偏移量是以UTC/ 格林威治时间为基准的 |
Period | 代表时间段 |
ZoneOffset | 代表时区偏移量,比如:+8:00 |
Clock | 代表时钟,比如获取目前美国纽约的时间 |
在Java 8之前,所有东西都是面向对象的。除了原语之外,java中的 所有内容都作为对象存在。对方法/函数的所有调用都是使用对象或类引用进行的。
方法/功能本身并不是独立存在的。
使用Java 8,引入了函数式编程。所以我们可以使用匿名函数。Java是一种一流的面向对象语言。除了原始数据类型之外,Java中的所有内容都是一个对象。即使是一个数组也是一个对象。每个类都创建对象的实例。没有办法只定义一个独立于Java的函数/方法。无法将方法作为参数传递或返回该实例的方法体。
Java 8引入了一个新的容器类java.util.Optional 。如果该值可用,它将包装一个值。如果该值不可用,则应返回空的可选项。因此它代表空值,缺失值。这个类有各种实用方法,如isPresent(),它可以帮助用户避免使用空值检查。由于不直接返回值,而是返回包装器对象,所以用户可以避免空指针异常。
流操作可以分为两部分:
多个中间操作可以连接起来形成一个流水线,除非流水线上触发终止操作,否则中间操作不会执行任何处理!而在终止操作时一次性全部处理,称作“惰性求值”。
Stream API借助于同样新出现的Lambda表达式。同时,它提供串行和并行两种模式进行汇聚操作,并发模式能够充分利用多核处理器的优势,使用fork/join并行方式来拆分任务和加速处理过程。
流操作可以分为两部分:
多个中间操作可以连接起来形成一个流水线,除非流水线上触发终止操作,否则中间操作不会执行任何处理!而在终止操作时一次性全部处理,称作“惰性求值”。
stream并行原理: 其实本质上就是在ForkJoin上进行了一层封装,将Stream 不断尝试分解成更小的split,然后使用fork/join 框架分而治之, parallize使用了默认的ForkJoinPool.common 默认的一个静态线程池.
Lambda Expression可以定义为允许用户将方法作为参数传递的匿名函数。Lambda函数没有访问修饰符(私有,公共或受保护),没有返回类型声明和没有名称。
Lambda表达式允许用户将“函数”传递给代码。所以,假设我们的代码具有一些复杂的循环/条件逻辑或工作流程。使用lambda表达式,在那些有难度的地方,可以得到很好的解决。
优点:
1、代码更加简洁,更少的代码行。
2、效率更高。通过使用具有多核的CPU,用户可以通过使用lambda并行处理集合来利用多核CPU。
2、减少匿名内部类的创建,节省资源;
缺点:
1、不熟悉Lambda表达式的语法的人,不太容易看得懂;
2、虽然代码更加简洁,但可读性差,不利于维护;
使用JDK8时,permGen空间已被删除。那么现在将元数据信息存储在哪里?此元数据现在存储在本机内存中,称为“MetaSpace”。该内存不是连续的Java堆内存。
整个永久代有一个 JVM 本身设置固定大小上线,无法进行调整,而元空间使用的是直接内存,受本机可用内存的限制,并且永远不会出现java.lang.OutOfMemoryError。你可以使用-XX:MaxMetaspaceSize 标志设置最大元空间大小,默认值为 unlimited,这意味着它只受系统内存的限制。-XX:MetaspaceSize 调整标志定义元空间的初始大小如果未指定此标志,则 Metaspace 将根据运行时的应用程序需求动态地重新调整大小。
Java SE 8具有以下功能,使其优于其他功能:
它具有更易读和简洁的代码。
它编写并行代码。它提供了更多可用的代码。
它具有改进的性能应用程序。
它支持编写包含促销的数据库。
Lambda表达式可以一次携带零个,一个或甚至多个参数。
另一方面,Lambda箭头运算符使用图标“->”将Lambda表达式拆分成两部分;
1.各种流
字节流----FileInputStream、FileOutputStream
字符流----FileReader、FileWriter
缓冲流----BufferedInputStream、BufferReader
转换流----InputStreamReader、OutputStreamWriter
序列化流--ObjectOutputStream、ObjectInputStream
(巧计:InputStream是字节流家族的,Reader是字符流家族的,InputStreamReader既含字节流又含字符流所以就是来回切换的流,就是转换流)
2.流之间的继承关系
字节输入流、字节输出流
FileInputStream extends InputStream
FileOutputStream extends OutputStream
字符输入流、字符输出流(字符流家族一定含有reader或writer)
FileReader extends InputStreamReader extends Reader
FileWriter extends OutputStreamWriter extends Writer
缓冲流
BufferedOutputStream extends OutputStream
BufferedInputStream extends InputStream
BufferedWriter extends Writer
BufferedReader extends Reader
转换流(用来编码的)
InputStreamReader extends Reader
OutputStreamWriter extends Writer
序列化流:构造方法的参数是字节流
ObjectOutputStream extends OutputStream
ObjectInputStream extends InputStream
(1)按照流的方向:输入流(inputStream)和输出流(outputStream);
(2)按照流是否直接与特定的地方(如磁盘、内存、设备等)相连,分为节点流和处理流两类。
(3)按照处理数据的单位: 字节流和字符流。
1.节点流 直接与数据源相连,用于输入或者输出
2.处理流:在节点流的基础上对之进行加工,进行一些功能的扩展
3.处理流的构造器必须要 传入节点流的子类
节点流:
字节流----FileInputStream、FileOutputStream
字符流----FileReader、FileWriter
处理流:
缓冲流----BufferedInputStream、BufferReader
转换流----InputStreamReader、OutputStreamWriter
序列化流--ObjectOutputStream、ObjectInputStream
序列化:把Java对象转换为字节序列的过程。
反序列化:把字节序列恢复为Java对象的过程。
当两个进程在进行远程通信时,彼此可以发送各种类型的数据。无论是何种类型的数据,都会以二进制序列的形式在网络上传送。发送方需要把这个Java对象转换为字节序列,才能在网络上传送;接收方则需要把字节序列再恢复为Java对象。只能将支持 java.io.Serializable 接口的对象写入流中。
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("10_IO\\person.txt"));
oos.writeObject(new Person("小美女",18));
序列化的实现,将需要被序列化的类实现Serializable 接口,该接口没有需要实现的方法, implements Serializable 只是为了标注该对象是可被序列化的,然后使用一个输出流(如: FileOutputStream)来构造一个 ObjectOutputStream(对象流)对象,接着使用 ObjectOutputStream 对象的 writeObject(Object obj)方法就可以将参数为 obj 的对象写出(即保存其状态),要恢复的话则用输入流。
①字节流主要是操作 byte 类型数据,字节流处理byte或byte数组;
字符流处理的单元为 2 个字节的 Unicode 字符,字符流处理char或char数组或String字符串。
②字符流是由 Java 虚拟机将字节转化为 2 个字节的 Unicode 字符为单位的字符而成的,所以它对多国语言支持性比较好!如果是音频文件、图片、歌曲,就用字节流好点,如果是关系到中文(文本)的,用字符流好点。在程序中一个字符等于两个字节,java 提供了 Reader、Writer 两个专门操作字符流的类。
②字节流可以处理所有类型数据,如:图片,MP3,AVI视频文件,而字符流只能处理字符数据。只要是处理纯文本数据,就要优先考虑使用字符流,除此之外都用字节流。
1.PrintStream是OutputStream的子类,PrintWriter是Writer的子类。对于使用print系列方法的场合,二者非常相像。
PrintStream和PrintWriter的autoflushing机制有点不同,前者在输出byte数组、调用println方法、输出换行符或者byte值10(即\n)时自动调用flush方法,后者仅在调用println方法时发生autoflushing。
System.out.println()就是调用的PrintStream的println方法。这里的out就是printstream对象,println方法会自动清空缓存
2.BufferedWriter字符缓冲输出流,只能对字符流进行操作。如果要对字节流操作,则使用BufferedInputStream。
不用缓冲流是读一个返回一个,现在有了缓冲流都是把读到的字节或字符先存在数组缓冲区,再一次返回多个字节或字符。
它通过newLine()进行换行操作。BufferedWriter中的字符流必须通过调用flush方法才能把内部缓冲区中的数据,刷新到文件中。
1.流一旦打开就必须关闭,使用close方法
2.放入finally语句块中(finally 语句一定会执行)
3.多个流互相调用只关闭最外层的流
同步就是B任务依赖A任务时,只有A任务完成后,B任务才能执行。这是一种可靠的任务序列。要么成功都成功,失败都失败,两个任务的状态可以保持一致。
异步就是B任务依赖A任务时,A任务执行时B任务也会立即执行。至于被依赖的A任务最终是否真正完成,B也无法确定,所以它是不可靠的任务序列。
举例:
同步和异步关注的是消息通信机制
你打电话问书店老板有没有《分布式系统》这本书,如果是同步通信机制,书店老板会说,你稍等,”我查一下",然后开始查啊查,等查好了(可能是5秒,也可能是一天)告诉你结果(返回结果)。
而异步通信机制,书店老板直接告诉你我查一下啊,查好了打电话给你,然后直接挂电话了(不返回结果)。然后查好了,他会主动打电话给你。在这里老板通过“回电”这种方式来回调。
阻塞与非阻塞主要是从 CPU 的消耗上来说的,阻塞就是 A任务执行时,B要阻塞等待。
非阻塞就是CPU在执行A任务时,也可以执行其它任务(比如B任务)。
虽然表面上看非阻塞的方式可以明显的提高 CPU 的利用率,但是也带了另外一种后果就是系统的线程切换增加。增加的 CPU 使用时间能不能补偿系统的切换成本需要好好评估。
举例:
阻塞和非阻塞关注的是程序在等待调用结果(消息,返回值)时的状态.
你打电话问书店老板有没有《分布式系统》这本书,你如果是阻塞式调用,你会一直把自己“挂起”,直到得到这本书有没有的结果,如果是非阻塞式调用,你不管老板有没有告诉你,你自己先一边去玩了, 当然你也要偶尔过几分钟check一下老板有没有返回结果。在这里阻塞与非阻塞与是否同步异步无关。跟老板通过什么方式回答你结果无关。
1.小明把水壶放到火上,然后在那傻等水开。(同步阻塞)
2.小明把水壶放到火上,然后去客厅看电视,时不时的去厨房看看水开没有。(同步非阻塞)
小明还是觉得自己有点傻,于是变高端了,买了一个水烧开了会响的水壶。水开之后能大声的发出响声提示人水开了。。。
3.小明把响水壶放到火上,然后在那傻等水开。(异步阻塞)
4.小明把响水水壶放到火上,去客厅看电视,水壶响之前不再去看它了,响了再去处理。(异步非阻塞)
所谓同步异步,只是对于水壶而言。普通水壶,同步;响水壶,异步。
虽然都能干活,但响水壶可以在自己完工之后,提示小明水开了。这是普通水壶所不能及的。同步只能让调用者去轮询自己(情况2中) ,造成小明效率的低下。
所谓阻塞非阻塞,仅仅对于小明而言。傻等的小明,阻塞;看电视的小明,非阻塞。
情况1和情况3中小明就是阻塞的,有人喊他都不知道。虽然3中响水壶是异步的,可对于立等的小明没有太大的意义。所以一般异步是配合非阻塞使用的,这样才能发挥异步的效用。
同步异步:同步是指一个时间点只能有一个程序在占用CPU,而异步是指可以有多个程序并行(可以很好的利用操作系统的多核)
阻塞非阻塞:阻塞是指操作系统发出一个调用/操作之后,必须等到此调用返回结果后才返回。而非阻塞是指在操作系统发出一个调用/操作后,不用等到执行完毕得到结果才返回,而是立即返回,然后可以执行其他的调用/操作。
通道是一种管理I/O操作的控制器,它使主机(CPU和内存)与I/O操作之间达到更高的并行程度。
缓冲区实质上是一个数组。在读取数据时,它是直接读到缓冲区中的。在写入数据时,它是写入到缓冲区中的。
种类:ByteBuffer、CharBuffer、ShortBuffer、IntBuffer、LongBuffer、FloatBuffer、DoubleBuffer
这一块严重涉及到计算机网络的知识,还有那些BIO、NIO、AIO都需要你看视频去专题掌握
文章浏览阅读1.5w次,点赞10次,收藏129次。文章目录目录模型评估评价指标1.分类评价指标acc、recall、F1、混淆矩阵、分类综合报告1.准确率方式一:accuracy_score方式二:metrics2.召回率3.F1分数4.混淆矩阵5.分类报告6.kappa scoreROC1.ROC计算2.ROC曲线3.具体实例2.回归评价指标3.聚类评价指标1.Adjusted Rand index 调整兰德系数2.Mutual Informa..._model.score
文章浏览阅读344次。因工作需要,在Apache上使用,重新学习配置mod_jk1. 分别安装Apache和Tomcat:2. 编辑httpd-vhosts.conf: LoadModule jk_module modules/mod_jk.so #加载mod_jk模块 JkWorkersFile conf/workers.properties #添加worker信息 JkLogFil_apache mod_jk 虚拟
文章浏览阅读335次。待老夫kotlin大成,扩展:MotionLayout 与 CoordinatorLayout,DrawerLayout,ViewPager 的 交互众所周知,MotionLayout 的 动画是有完成度的 即Progress ,他在0-1之间变化,一.CoordinatorLayout 与AppBarLayout 交互时,其实就是监听 offsetliner 这个 偏移量的变化 同样..._android onoffsetchanged
文章浏览阅读8.3k次,点赞3次,收藏19次。【转】多核处理器的工作原理及优缺点《处理器关于多核概念与区别 多核处理器工作原理及优缺点》原文传送门 摘要:目前关于处理器的单核、双核和多核已经得到了普遍的运用,今天我们主要说说关于多核处理器的一些相关概念,它的工作与那里以及优缺点而展开的分析。1、多核处理器 多核处理器是指在一枚处理器中集成两个或多个完整的计算引擎(内核),此时处理器能支持系统总线上的多个处理器,由总..._多核处理器怎么工作
文章浏览阅读306次。1. eclipse配置lombok 拷贝lombok.jar到eclipse.ini同级文件夹下,编辑eclipse.ini文件,添加: -javaagent:lombok.jar2. myeclipse配置lombok myeclipse像eclipse配置后,定义对象后,直接访问方法,可能会出现飘红的报错。 如果出现报错,可按照以下方式解决。 ..._eclispe每次运行个新项目都需要重新配置lombok吗
文章浏览阅读1.2w次,点赞31次,收藏126次。#注意:笔者在2021/11/11当天调试过这个代码是可用的,由于pdfminer版本的更新,网络上大多数的语法没有更新,我也是找了好久的文章才修正了我的代码,仅供学习参考。1、把pdf文件移动到本代码文件的同一个目录下,笔者是在pycharm里面运行的项目,下图中的x1文件夹存储了我需要转换成文本文件的所有pdf文件。然后要在此目录下创建一个存放转换后的txt文件的文件夹,如图中的txt文件夹。2、编写代码 (1)导入所需库# coding:utf-8import ..._python批量读取文字并批量保存
文章浏览阅读1.4k次。http://blog.csdn.net/pipisorry/article/details/52902234Scala 访问修饰符Scala 访问修饰符基本和Java的一样,分别有:private,protected,public。如果没有指定访问修饰符符,默认情况下,Scala对象的访问级别都是 public。Scala 中的 private 限定符,比 Java 更严格,在嵌套类情况下,外层_scala ===运算符
文章浏览阅读2.6k次,点赞7次,收藏19次。ER图导出为PDF或图片格式_数据库怎么导出er图
文章浏览阅读655次。CREATE OR REPLACE TRIGGER Trg_ReimFactBEFORE UPDATEON BP_OrderFOR EACH ROWDECLAREPRAGMA AUTONOMOUS_TRANSACTION;--自制事务fc varchar2(255);BEGINIF ( :NEW.orderstate = 2AND :NEW.TransState = 1 ) THENBEG..._oracle触发器更新同一张表
文章浏览阅读513次。目录概念debouncethrottle实现debouncethrottle应用场景debouncethrottle场景举例debouncethrottle概念debounce字面理解是“防抖”,何谓“防抖”,就是连续操作结束后再执行,以网页滚动为例,debounce要等到用户停止滚动后才执行,将连续多次执行合并为一次执行。throttle字面理解是“节流”,何谓“节流”,就是确保一段时..._throttle和debounce应用在哪些场景
文章浏览阅读526次。regex() $regex 正则表达式用于模式匹配,基本上是用于文档中的发现字符串 (下面有例子)注意:若未加 @Field("名称") ,则识别mongdb集合中的key名为实体类属性名。也可以对数组进行索引,如果被索引的列是数组时,MongoDB会索引这个数组中的每一个元素。也可以对整个Document进行索引,排序是预定义的按插入BSON数据的先后升序排列。save: 若新增数据的主键已经存在,则会对当前已经存在的数据进行修改操作。_java 操作mongodb
文章浏览阅读1k次。今天push代码到github仓库时出现这个报错TACKCHEN-MB0:tc-image tackchen$ git pushremote: Support for password authentication was removed on August 13, 2021. Please use a personal access token instead.remote: Please see https://github.blog/2020-12-15-token-authentication_git push remote: support for password authentication was removed on august 1