Java-

目录

eclipse快捷键

eclipse中补全代码快捷键,默认Alt+/
get、set方法: alt + shift +s

代码助手:Ctrl+Space(简体中文操作系统是Alt+/)
快速修正:Ctrl+1
单词补全:Alt+/
打开外部Java文档:Shift+F2
显示搜索对话框:Ctrl+H
快速Outline:Ctrl+O
打开资源:Ctrl+Shift+R
打开类型:Ctrl+Shift+T
显示重构菜单:Alt+Shift+T

上一个/下一个光标的位置:Alt+Left/Right
上一个/下一个成员(成员对象或成员函数):Ctrl+Shift+Up/Down
选中闭合元素:Alt+Shift+Up/Down/Left/Right
删除行:Ctrl+D
在当前行上插入一行:Ctrl+Shift+Enter
在当前行下插入一行: Shift+Enter
上下移动选中的行:Alt+Up/Down

组织导入:Ctrl+Shift+O

##  面试题
①final、finally、finalize的区别?
② == 和 equals() 的区别?
# 一、Java基础编程
##  方法与对象的使用

### 1.面向对象编程(三条主线)
#### ①Java类及类的成员
类的成员:属性、方法、构造器、代码块、内部类
#### ②面向对象的三大特征
封装性、继承性、多态性、(抽象性)
#### ③其他关键字
this、super、static、final、abstract、interface、package、import 等

### 2.面向对象的两个要素
 ①类
对一类事物的描述,是抽象的、概念上的定义
②对象
(对象必须基于类来创建。)
是实际存在的该类事物的每个个体,因而也称为实例(instance)

###  3.设计类
 其实就是设计类的成员
属性 = 成员变量 = field = 域、字段
方法 = 成员方法 = 函数 = method
创建类的对象 = 类的实例化 = 实例化类

### 4.类和对象的使用(面向对象思想落地的实现)
 ①创建类,设计类的成员
 ②创建类的对象
 ③通过 ”对象.属性“ 或 ”对象.方法“ 调用对象的结构
 ④如果创建了一个类的多个对象,则每个对象都独立的拥有一套类的属性。(非static的)意味着:如果我们修改了一个对象的属性a的值,但不会影响另外一个对象属性a的值。

###  5.对象的内存解析
![在这里插入图片描述](https://img-blog.csdnimg.cn/f9583d7b23f2444ab0ae86b1c232e527.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA6bG85LuUXw==,size_20,color_FFFFFF,t_70,g_se,x_16#pic_center)
![在这里插入图片描述](https://img-blog.csdnimg.cn/bbeb5175947548cbad16ffa876bc9cc4.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA6bG85LuUXw==,size_20,color_FFFFFF,t_70,g_se,x_16#pic_center)
### 6.类中属性的使用
#### 属性(成员变量) VS   局部变量
1、相同点:
  ①定义变量的格式:数据类型 变量名 =  变量值
  ②先声明,后使用
  ③变量都有其对应的作用域
2、不同点:
**①  在类中声明的位置不同**
      属性:直接定义在类的一对{}内
      局部变量:声明在方法内、方法形参、代码块内、构造器形参、构造器内部的变量。 

**②关于权限修饰符的不同**
 属性:可以在声明属性时,指明其权限,使用权限修饰符。
 常用的权限修饰符:private、public、缺省、protected
 局部变量:不可以使用权限修饰符

**③默认初始化值的情况不同**
属性:类的属性,根据其类型,都有默认初始化值。
   整型(byte、short、int、long) : 0
   浮点型 (float、double) : 0.0
   字符型 (char) : 0 (或 '\u0000')
   布尔型 (boolean) : false
引用数据类型(类、数组、接口) : null

局部变量:没有默认初始化值。
  意味着,我们在调用局部变量之前,一定要显式赋值。
  特别的:形参在调用时,我们赋值即可。
 
 **④在内存中加载的位置不同**
 属性:加载到堆空间中 (非static)
  局部变量:加载到栈空间
  
### 7.类中方法的声明和使用

#### ①方法的声明:
权限修饰符 返回值类型 方法名(形参列表){ 方法体 }
例:public static void main(String[] args){}  
注:static、final、abstract 来修饰方法。

#### ②权限修饰符:
java规定的4种权限修饰符:private 、public、protected、缺省
->>封装性时,细研

#### ③返回值类型:
有返回值 VS 没有返回值
①如果方法有返回值,则必须在方法声明时,指定返回值的类型。  同时,方法中,需要使用return关键字来返回指定类型的变量或常量。"return 变量/常量"
②如果方法没有返回值,则方法声明时, 使用void来表示	. 通常,没有返回值的方法中,就不使用return 但是,如果使用的话,只能"return;" 表示结束此方法的意思.

#### ④方法名: 
属于标识符,遵循标识符的规则和规范,"见名知意"
形参列表: 方法可以声明0个 、1个或多个形参。
格式: (数据类型1  形参1 , 数据类型2  形参 2 , .....)

#### ⑤return关键字的使用: 
 使用范围:  使用在方法中
 作用 :
  ①结束方法 
  ②针对于有返回值类型的方法,使用"return 常量/变量(数据)" 方法返回所要的  数据. 注意点: return关键字后面不可以声明执行语句.

#### ⑥方法的使用:
 可以调用当前类的属性或方法
 特殊的: 方法A 中又调用了方法A : 递归方法 .
 方法中,不可以再定义方法

```java
package lz3;

public class CustomerTest {

	public static void main(String[] args) {
		
		Customer C1 = new Customer() ;
        C1.eat();
        C1.name="yyz";
        System.out.println(C1.getName());
        System.out.println(C1.getNation("中国")) ;
        C1.sleep(8);
	}
	
}
	

class Customer {
	 //属性
	 
	 String name ;
	 int age ;
	 boolean isMale ;
	 
	 //方法 
	 public void eat () {
		 System.out.println("可以吃饭");
	 }
	 public void sleep(int hour) {
		 System.out.println("休息了" + hour + "小时");
		 eat();
	 }
	 public String getName() {
		 return name;
	 }
	 public String getNation(String nation) {
		 String info = "我的国籍是:" + nation;
		 return info ;
	 }
	 
}

总结

面向对象思想编程内容的三条主线分别是什么?

面向对象编程思想内容的三条主线:
①类及类的成员:属性->方法->构造器->代码块->内部类

②面向对象的三大特征:封装->继承->多态

③其他关键字:this->super->abstract->interface->package->import

谈谈你对面向对象中类和对象的理解,并指出二者的关系?

类:抽象上的、概念上的内容
对象:实实在在存在的一个个体。对象是由类派生出来的

面向对象编程思想的体现一:类和对象的创建和执行操作有哪三步?

①创建类
②类的实例化
③调用对象的结构:”对象.属性“ ”对象.方法“

虚拟机栈,即为平时提到的栈结构。我们将局部变量存储在栈结构中
堆,我们将new出来的结构(比如:数组、对象)加载在堆空间中。补充:对象的属性(非static的)加载在堆空间中
方法区:类的加载信息、常量池、静态域

理解”万事万物皆对象“ ?

1、在Java语言范畴中,我们都将功能、结构等封装到类中,通过类的实例化,来调用具体的结构

2、涉及到Java语言与前端Html、后端的数据库交互时,前后端的结构在Java层面交互时,都体现为类、对象

内存解析的说明

1、引用类型的变量,只可能储存两类值:null 或 地址值(含变量的类型)

匿名对象的使用

1、理解:我们创建的对象,没有显示的赋给一个变量名。即为匿名对象
2、特征:匿名对象只能调用一次
3、使用:或将匿名对象的地址值赋给另一个对象,可调用多次

可变个数形参的方法

1、可变个数形参的格式:数据类型 ... 变量名
2、当调用可变个数形参的方法时,传入的参数可以是:0个,1个,2个…
3、可变个数形参的方法与本类中方法名相同,形参不同的方法之间构成重载
4、可变个数形参的方法与本类中方法名相同,形参类型也相同的数组之间不构成重载。
5、可变个数形参在方法的形参中,必须声明在末尾
6、可变个数形参在方法的形参中,最多只能声明一个可变形参

package lz94;

public class DeFormableGinseng {

	public static void  main(String[] args) {
		De f1 = new De() ;
		f1.function(1,"2","3");
		
	}
}

class De {
	
	public void function (int a,String  b) {
		System.out.println("int , String");
	}
	
	public void function (int a ,String ... b) {
		System.out.println("int , String...");
	}
	
}
关于变量的赋值:

1、如果变量是基本数据类型,此时赋值的是变量所保存的数据值
2、如果变量是引用数据类型,此时赋值的是变量所保存的数据的地址值(即,被赋值的那个变量的栈中的地址值指向的堆中的储存空间与赋值的那个值所指向的储存空间一样)

package lz94;

public class VariableAssignment {

	public static void main (String[] args) {
		variable m1 = new variable() ;
		variable c = m1;
		m1.a = 9 ;
		m1.methods();
		c.a =100 ;
		c.methods();
		m1.methods();
		
		
	}
}

class variable{
	int a ;
	
	public void methods() {
		System.out.println("a="+a);
	}
	
	
	
}
方法的形参的传递机制,值传递

1、形参:方法定义时,声明的小括号内的参数
实参:方法调用时,实际传递给形参的数据

2、值传递机制:
如果参数是基本数据类型,此时实参赋给形参的是,实参真实存储的数据值。
如果参数是引用数据类型,此时实参赋给形参的是实参存储数据的地址值。

封装性

封装性的设计思想

隐藏对象内部的复杂性,只对外公开简单的接口。便于外界调用,从而提高系统的可扩展性、可维护性。
通俗化:把该隐藏的隐藏起来,该暴露的暴露出来。

响应java程序设计追求 “高内聚,低耦合”
高内聚:类的内部数据操作细节自己完成,不允许外部干涉
低耦合:仅对外暴露少量的方法用于使用

封装性的体现一

我们将类的属性xxx私有化(private), 同时,提供公共的(public)方法来获取(getXxx)和设置(setXxx)此属性的值。

/* Encapsulation 封装性
 * animal 动物 
 * 
 * 封装性的体现:①属性私有化 ②不对外暴露的私有的方法 ③单例模式 .......
 * */


public class animal {

	public static void main (String[] args) {
		rabble r1 = new rabble();
		//r1.name = "jack";
		//使用set方法给Age赋值
		r1.setAge(11);
		//调用eat方法
		r1.eat();
		//查看Age属性的值
		System.out.println(r1.getAge());
	} 
	
}

class rabble {
	 private String name ;
	 private int Age ;
	 private int Gender ;
	// get() 与 set()方法 
	public int getAge() {
		return Age ;
	} 
	public void setAge(int a) {
		Age = a ;
	}
	 
	public void eat () {
		System.out.println(name +"会吃饭"+",今年"+Age+"岁了"+",Gender是"+Gender);
	}
}

封装性的体现二

不对外暴露的私有的方法

封装性的体现三

单例模式(将构造器私有化)【static关键字后解锁 】

封装性的体现四

如果不希望类在包外被调用,可以将类设置为缺省的。

封装性的体现…

封装性的体现无处不在,只要是涉及到4种权限的修饰符,便体现了封装性。
若没有Java的四种权限,便没有了封装性。
封装性的体现,需要权限修饰符来配合。

Java规定的四种权限修饰符

①java规定的4种权限(从小到大排列):private->缺省->protected->public

②4种权限都可以用来修饰类及类的内部结构:属性、方法、构造器、内部类

③修饰的话,只能使用: 缺省、public。

④具体的修饰范围
在这里插入图片描述

总结封装性

Java提供了四种权限修饰符来修饰类及类的内部结构,体现类及类的内部结构在被调用时可见性的大小。

构造器

1. eclipse构造器快捷键

①Alt+shift+s

2. 构造器功能

①创建对象
②初始化对象信息

3. 详细说明

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

public class TheConstructor {

	public static void main(String[] args) {
		person p1 = new person() ;
		p1.eat();
		person p2 = new person("jack",30) ;
		p2.walk();
		p1.setName("Tom ");
		p1.walk();
	}

}



class person {
	//属性
	private String name ;
	private int Age ;
	
	//构造器
	public person() {
    //空参构造器
	}
	private person(String n) {
		name = n ;
	}
	public person(String m,int a) {
		name = m ;
		Age = a ;
	}
	
	//方法
	public void setName(String n) {
		name = n ;
	}
	
	public void eat () {
		System.out.println("可以吃饭");
	}
	public void walk() {
		System.out.println(name+"会走路");
	}
}
package Triangle;

public class Triangle1 {

	public static void main(String[] args) {
		TrianglE T1 = new TrianglE(4,6);
		System.out.println(T1.getBase());    
		System.out.println(T1.getHeight());   
		T1.calculate();
	} 

}


class TrianglE{
	//属性
	private int base ;
	private int height;
	
	//构造器
	public TrianglE() {
		
	}
	public TrianglE (int a,int b) {
		base = a ;  
		height = b ;
	}
	
	//方法
	public void setBase(int a) {
		base = a ;
	}
	public int getBase() {
		return base;
	}
	public void setHeight(int a) {
		height = a ;
	}
	public int getHeight() {
		return height;
	}
	public void  calculate () {
		int s ;
		s=(base * height)/2 ;
		System.out.println("底为:"+base +",高为:"+height+"的三角形的面积为:"+s);
	}
}

4. 属性赋值的先后顺序

①默认初始化
②显示初始化
③构造器赋值
④通过“对象.方法” 或 “对象.属性” 的方式。赋值
以上操作的先后顺序:① - ② - ③ - ④

JavaBean

JavaBean定义

JavaBean 是一种Java语言写成的可重用组件。

所谓JavaBean,是指符合如下标准的Java类:
①类是公共的
②有一个无参的公共的构造器
③有属性,且有对应的get、set方法

JavaBean作用

用户可以使用JavaBean将功能、处理、值、数据库访问和其他任何可以用Java代码创造的对象进行打包,并且其他的开发者可以通过内部的JSP页面、其他JavaBean、apple程序或者应用来使用这些对象。用户可以认为JavaBean提供了一种随时随地的复制和粘贴的功能,而不用关心任何改变。

package JavaBean;
//公共类
//有一个无参的构造器
//有属性的,且有对应get、set方法的

public class Bean {

	public static void main(String[] args) {
		
	}
	//属性
	private String name ;
	private int a;
	
	//无参构造器
	public Bean() {
		
	}
	//get、set方法
	public String getName() {
		return name ;
	} 
	public void setName(String c) {
		name = c ;
	}
	public int getA() {
		return a ;
	} 
	public void setA(int c) {
		a = c ;
	}
}

关键字 this

1、可以调用的结构

this 可以用来修饰:属性、方法、构造器

2、this 修饰属性和方法

this理解为:当前对象 或 当前正在创建的对象

2.1 在类的方法中,我们可以使用“this.属性” 或 “this.方法” 的方法,调用当前对象属性或方法。但是通常情况下,我们都选择省略“this.”。特殊情况下,如果方法的形参和类的属性同名时,我们必须显示的使用“this.变量”的方式,表明此变量是属性,而非形参。

2.2 在类的构造器中,我们可以使用“this.属性” 或 “this.方法” 的方法,调用当前正在创建的对象属性或方法。但是通常情况下,我们都选择省略“this.”。特殊情况下,如果方法的形参和类的属性同名时,我们必须显示的使用“this.变量”的方式,表明此变量是属性,而非形参。

3、this调用构造器

①:我们在类的构造器中,可以显示的使用 “this(形参列表 )” 方式,调用本类中指定的其他构造器
②:构造器中不能通过 “this(形参列表)” 方式调用自己
③:如果一个类中有n个构造器,则最多有n-1 个构造器使用了 “this(形参列表 )” ,剩下的那一个是super()。
④:规定: “this(形参列表 )” 必须声明在当前构造器的首行
⑤:构造器内部,最多只能声明一个 “this(形参列表 )” ,用来调用其他的构造器

MVC设计模式

模型层 model 主要处理数据
控制层 controller 处理业务逻辑
视图层 view 显示数据

在这里插入图片描述

复习问题

①构造器的作用是什么?使用中有哪些注意事项?
②关于类的属性的赋值,有几种赋值的方式。以及赋值的先后顺序
③this 关键字可以用来调用哪些结构
④java中目前学习涉及到的四种权限修饰符都有什么?并说各自的权限范围

继承性

1、继承性的特点

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

2、继承性的格式

calss A extends B{}
# A为子类,B为父类

2、继承性的体现

2.1 体现:一旦子类A继承父类B以后,子类A就获取了父类B中声明的所有的属性和方法。
特别的,父类中声明为private的属性或方法,子类继承父类后,仍然认为获取了父类中私有的结构。但因为封装性的影响,使得子类不能直接调用父类的结构。
2.2 子类继承父类以后,还可以声明自己特有的属性和方法:实现功能的拓展

3、继承性的规定

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

4、java.lang.Objeck类(默认父类)(间接父类)

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

程序的调式——Debug调试

① 设置断点
设置方法:(对“行数”双击)
注:可设置多个断点
②右键-> debug as -> java application->yes
③常用操作

操作作用
step into 跳入(f5)进入当前行所调用的方法中
step over 跳过(f6)执行完当前行的语句,进入下一行
step return 跳回(f7)执行完当前行所在的方法,进入下一行
drop to frame回到当前行所在方法的第一行
resume 恢复执行完当前所在断点的所有代码,进入下一个断点,如果没有就结束

方法的重写——(子类重写父类同名同参的方法)

方法的重写(override / overwrite)

  1. 重写
    子类继承父类后,可以对父类中同名同参数的的方法。进行覆盖操作
  2. 应用
    重写以后,当创建子类对象以后,通过子类对象调用子父类中的同名同参数的方法时,实际执行的是子类重写父类的方法。

3. 重写的规定

方法的声明: 权限修饰符 返回值类型 方法名(形参列表) throws 异常的类型 {
//方法体
}
约定俗称:子类中的叫重写方法,父类中的叫被重写的方法

①子类重写的方法的方法名和形参列表与父类被重写的方法的方法名和形参列表相同
②子类重写的方法的权限修饰符不小于父类被重写的方法的权限修饰符
③返回值类型
a.父类被重写的方法的返回值类型是void,则子类重写的方法的返回值类型只能是void
b.父类被重写的方法的返回值类型是A类型,则子类重写的方法的返回值类型可以是A类或A类的子类
c.父类被重写的方法的返回值类型是基本数据类型(比如:double),则子类重写的方法的返回值类型必须是相同的
④子类重写的方法抛出的异常类型不大于父类被重写的方法抛出的异常类型

特殊情况:子类不能重写父类中声明为private权限的方法

  子类和父类中的同名同参数的方法要么都声明为非static的(考虑重写),要么都声明为static的(不是重写)。

super 关键字

1.super 理解为:父类的

2. super 可以调用的结构:属性、方法、构造器

3.super的使用

3.1 我们可以在子类的方法或构造器中。通过使用“super.属性”或“属性.方法”的方式,显示的调用父类中声明的属性或方法。但是,通常情况下,我们习惯省略"super."
3.2 特殊情况:当子类和父类中定义了同名的属性时,我们要想在子类中调用父类中声明的属性,则必须显示的使用“super.属性”的方式,表明调用的是父类中的属性。
3.3 特殊情况:当子类重写了父类中的方法以后,我们想在子类的方法中调用父类中被重写的方法时,则必须显示的使用"super.方法"的方式,表明调用的是父类中被重写的方法。

4. super调用构造器

4.1 我们可以在子类的构造器中显示的使用“super(形参列表)”的方式,调用父类中声明的指定的构造器
4.2 "super(形参列表)"的使用,必须声明在子类构造器的首行!
4.3 我们在类的构造器中,针对于"this(形参列表)"或"super(形参列表)“只能二选一,不能同时出现
4.4 在构造器的首行,没有显示的声明"this(形参列表)“或"super(新参列表)”,则默认调用的是父类中空参的构造器:super()
4.5 在类的多个构造器中,至少有一个类的构造器中使用了"super(形参列表)”,调用父类的构造器

子类对象实例化的过程

1.从结果上来看:就是(继承性)
 子类继承父类以后,就获取了父类中声明的属性或方法。
 创建子类的对象,在堆空间中,就会加载所有父类中声明的属性。
2. 从过程上来看

当我们通过子类的构造器创建子类对象时,我们一定会直接或间接的调用其父类的的构造器,进而调用父类的父类的构造器,直到调用了Java.lang.Object类中空参的构造器为止。正因为加载过所有的父类的结构,所以才可以看到内存中有父类中的结构,子类对象才可以考虑进行调用。

图示
在这里插入图片描述

3. 强调说明

需要明确的是:虽然创建子类对象时,调用了父类的构造器,但是至始至终只创建过一个对象,那个对象就是 新new出来的子类对象。
在这里插入图片描述

多态性

1.多态性的理解

可以理解为一个事物的多种形态。

2.何为多态性

对象的多态性:父类的引用指向子类的对象 (或子类的对象赋给父类的引用)

 举例:
 person p = new Man();
 Object obj = new Date();

3.多态的使用 (虚拟方法调用)

虚拟方法调用:
有了对象的多态性以后,我们在编译期,只能调用父类声明的方法,但在运行期,我们实际执行的是子类重写父类的方法。
总结:编译,看左边;运行,看右边

虚拟方法调用(Virtual Method Invocation)
●正常的方法调用
Person e = new Person();
e.getlnfo();
Studente = new Student();
e.getlnfo();
●虚拟方法调用(多态情况下)

子类中定义了与父类同名同参数的方法,在多态情况下,将此时父类的方法称为虚拟方法,父类根据赋给它的不同子类对象,动态调用属于子类的该方法。这样的方法调用在编译期是无法确定的。

Person e = new Student();
e.getInfo();  //调用Student类的getInfo()方法
●编译时类型和运行时类型

编译时e为Person类型,而方法的调用是在运行时确定的,所以调用的是Student类的getInfo(方法。——动态绑定

4.多态性的使用前提:

①类的继承关系
②方法的重写

5. 多态性的应用举例

举例一:

public void fun(Animal animal){ //Animal animal = new Dog();
	animal.eat();
	animal.shout();
}

举例二:

public void method(Object obj){
	
}

举例三:

calss Driver{
	public  void doData(Connection conn){//conn = new MySqlConnection(); /conn = new OracleConnection(); 
		//操作数据库
//		conn.method1();
//		conn.method2();
//		conn.method3();
	}
}

6. 多态性的注意点

对象的多态性,只适用于方法,不适用于属性(编译和运行都看左边)

7. 多态性的体现

package learning10_6;

//测试类
public class AnimalTest {
	
	public static void main(String[] args) {
		AnimalTest a1 = new AnimalTest(); 
		a1.fun(new Dog());
		a1.fun(new Cat());
	}
	public void fun(Animal animal) { //Animal animal = new Dog() 匿名对象
		animal.eat();
		animal.shout();
	}
}

//父类
class Animal{
	public void eat() {
		System.out.println("动物.吃");
	}
	public void shout() {
		System.out.println("动物.叫");
	}
}
//子类
class Dog extends Animal{
	public void eat() {
		System.out.println("小狗吃骨头");
	}
	public void shout() {
		System.out.println("汪!汪!汪!");
	}
}
//子类
class Cat extends Animal{
	public void eat() {
		System.out.println("小猫吃鱼儿");
	}
	public void shout() {
		System.out.println("喵!喵!喵!");
	}
}

8. 多态性-问题

/*1.若子类重写了父类方法,就意味着子类里定义的方法彻底覆盖了父类里的同名方法,
 * 系统将不可能把父类里的方法转移到子类里:编译看左边,运行看右边
 *2.对于实例变量则不存在这样的现象,即使子类里定义了与父类完全相同的实例变量,
 *这个实例变量依然不可能覆盖父类中定义的实例变量:编译运行都看左边。 
 */
package learning10_7;
//-----------------------------------------------------
class MissZhu{
	int count = 10 ;
	
	public void learing() {
		System.out.println(this.count);
	}
}
//-----------------------------------------------------
class ClassmateFang extends MissZhu{
	int count = 20 ;
	
	public void learing() {
		System.out.println(this.count);
	}
}
//-----------------------------------------------------
public class problemTest {
	public static void main(String[] args) {
		ClassmateFang c = new ClassmateFang();
		System.out.println(c.count); //①问
		c.learing(); //②问
		MissZhu m = c ;
		System.out.println(m==c);//③问
		System.out.println(m.count);//④问
		m.learing();//⑤问
	}
}
//2020true1020

小结:方法的重载与重写

1.二者的定义细节,略
2.从编译和运行的角度看:
重载,是指允许存在多个同名方法,而这些方法的参数不同。编译器根据方法不同的参数表,对同名方法的名称做修饰。对于编译器而言,这些同名方法就成了不同的方法。它们的调用地址在编译期就绑定了。Java的重载是可以包括父类和子类的,即子类可以重载父类的同名不同参数的方法。所以:对于重载而言,在方法调用之前,编译器就已经确定了所要调用的方法,这称为“早绑定”或“静态绑定”;

而对于多态,只有等到方法调用的那一刻,解释运行器才会确定所要调用的具体方法,这称为“晚绑定”或“动态绑定”。

引用一句Bruce Eckel的话:“不要犯傻, 如果它不是晚绑定,它就不是多态。”

问题:区分方法的重写和重载?

答:
①二者的概念
②重载和重写的具体规则
③重载:不表现为多态性
重写:表现为多态性

重载,是指允许存在多个同名方法,而这些方法的参数不同。编译器根据方法不同的参数表,对同名方法的名称做修饰。对于编译器而言,这些同名方法就成了不同的方法。它们的调用地址在编译期就绑定了。Java的重载是可以包括父类和子类的,即子类可以重载父类的同名不同参数的方法。所以:对于重载而言,在方法调用之前,编译器就已经确定了所要调用的方法,这称为“早绑定”或“静态绑定”;
而对于多态,只有等到方法调用的那一刻,解释运行器才会确定所要调用的具体方法,这称为“晚绑定”或“动态绑定”。
引用一句Bruce Eckel的话:“不要犯傻, 如果它不是晚绑定,它就不是多态。”

instanceof 操作符

例:a instanceof A
效果:判断对象a是否是类A的实例。如果是,返回true;如果不是,返回false
使用情景:为了避免在向下转型时出现ClassCastException的异常,我们在向下转型之前,现进行instanceof的判断,一旦返回ture,就进行向下转型。如果返回false,不进行向下转型。
特别的:有类B是类A看,如果a instanceof A返回true,则a instanceof B也返回true。
在这里插入图片描述

package learning10_7;

public class Test_1 {
	public static void main(String[] args) {
		java_1 j1 = new java_2("龙同学",18,""); //虚拟方法的调用--当调用子父类同名同参数的方法时,
		                                       //实际执行的是子类重写父类方法
		j1.eat();
		j1.name = "";
//		j1.xxx ="" ; //不能调用子类所特有的方法、属性
// --问题点:有了对象的多态性以后,内存中实际上是加载了子类特有的的属性和方法的,但是由于变量声明为父类类型
// --,导致编译时,只能调用父类中声明的属性和方法。子类特有的属性和方法不能调用。  
// --------------so,如何才能调用子类特有的属性和方法?----------------------
//---------------答:向下转型:使用强制类型转换符。--------------------------		
		java_2 j2 = (java_2) j1 ;
		j2.xxx = "" ;
		j2.name = "鱼仔";
		j2.learn();
//---------------注:使用强制类型转换,可能出现ClassCastException的异常。		
//	    java_3 j3 = (java_3) j1 ;
//        j3.talk();
		
//-------------- instanceof  因此闪亮登场 -----------------------
//------ a instanceof A:判断对象a是否是类A的实例。如果是,返回true;如果不是,返回false。
//------使用意境:为了避免在向下转型时出现ClassCastException的异常,我们在向下转型之前,先进行instanceof
//----------------的判断,一旦返回true,就进行向下转型。如果返回false,不进行向下转型。
//-------有类B是类A的父类,若 a instanceof A 返回ture,则 a instanceof B 一定返回true。
		
		if(j1 instanceof java_3) {
			java_3 j3 = (java_3) j1 ;
			j3.talk();
			System.out.println("--------------java_3是类j1的实例----------------");
		}else {System.out.println("--------------java_3不是类j1的实例----------------");}
//-----------------------------------------------------------------------------------------------		
		if(j1 instanceof java_2) {
			java_2 j_2_2 = (java_2) j1 ;
			j_2_2.learn();
			System.out.println("--------------java_2_2是类j1的实例----------------");
		}
//-----------------------------------------------------------------------------------------------------
// -----------------------练习---------------------
		//①编译通过,运行通过   父类转子类
		  Object p1 = new java_3("",0,0);   //父级类p1
		  java_1 s1 =(java_1) p1 ;         //
		  s1.eat();
//           子类转父类
		  java_2 p4 =new java_2("",0,"");
		  java_1 s4 =(java_1) p4 ;
		  s4.eat();
	   //②编译通过,运行不过    父类转子类
		  java_1 p2 = new java_3("",0,0);  // 父级类 p2
//		  java_2 s2 = (java_2) p2 ;       //  将父级类p2-->子级类,并赋给子级类s2
// 		  s2.learn();                    //调用子级类方法,运行系统报错
	  //③编译不通过
//		  java_2 p3 = new java_3("",0,0);
		  
		
	}

}

Object类

java.lang.Object类

1.Object类是所有Java类的根父类

2.如果在类的声明中未使用extends关键字指明其父类,则默认父类为java.lang.Object类

3.Object 类中的功能(属性、方法)就具有通用性。
属性:无
方法:equals() / toString() / getClass() / hashCode() / clone() / finalize() / wait() 、notify()、notifyAll()

4.Object类只声明了一个空参的构造器

1. equals()方法

一、回顾 ‘==’ 运算符的使用:

(1) 可以使用在基本数据类型变量和引用数据类型变量中
(2)如果比较的是基本数据类型变量:比较两个变量保存的数据是否相等。(不一定类型要相同)
(3)如果比较的是引用数据类型变量:比较两个对象的地址是否相同,即两个引用是否指向同一个对象实体

二、equals()方法的使用:
(1)是一个方法,并非运算符
(2)只能适用于引用数据类型
(3)Objeck类中equals()的定义(源码):

   public boolean equals(Object obj){
			return (this == obj);
   }

说明:Object类中定义的equals()和==的作用是相同的:
比较两个对象的地址值是否相同.即两个引用是否指向同一个对象实体

4.像String、Date、File、包装类等都重写了Object类中的equals()方法。重写以后,比较的不是两个引用的地址是否相同,而是比较两个对象的”实体内容“是否相同。

5.通常情况下,我们自定义的类如果使用equals()的话,也通常是比较两个对象的“实体内容”是否相同。那么我们就需要对Object类中的equals()进行重写

2. 面试题: ==和equals的区别

① ==既可以比较基本类型也可以比较引用类型。对于基本类型就是比较值,对于引用类型就是比较内存地址

②equals方法,它是属于java.lang.Object类里面的方法,如果该方法没有被重写过默认也是==;而String等类的equals方法是被重写过的,而且String类在日常开发中用的比较多,便形成了equals是比较值的错误观点

③具体要看自定义类里有没有重写Object的equals方法来判断

④通常情况下,重写equals方法,会比较类中的相应属性是否都相等

3. 手动重写equals方法

class dog {
	String name;
	int age ;
	//重写equals()方法
	public boolean equals(Object obj) {
		if(obj ==  this) {
			return true;
		}
		if(obj instanceof dog) {
			 dog d1 = (dog)obj;
			 return this.age == d1.age && this.name.equals(d1.name);
		}
		return false ;
	}
}

开发中如何实现?:shift + Alt + s 自动生成

4. 重写equals()方法的原则

①对称性:如果x.equals(y)返回是"true” ,那么y.equals(x)也应该返回是"true"。

②自反性: x.equals(x)必须返回是"true"

③传递性:如果x.equals(y)返回是"true”,而且y.equals(z)返回是"true",那么z.equals(x)也应该返回是"true" 。

④一 致性: 如果x.equals(y)返回是"true",只要x和y内容一直不变,不管你重复x.equals(y)多少次,返回都是"true"。

⑤任何情况下, x.equals(null), 永远返回是“false";x.equals(和x不同类型的对象)永远返回是“false”。

5. toString()方法

Object类中toString()的使用:
 1.当我们输出一个对象的引用时,实际上就是调用当前对象的toString()
 2.Object类中toString的定义:
	public String toString(){
		return getClass().getName() + "@" +Integer.toHexString(hashCode());
	} 
 3.StringDateFile、包装类等都重写了Object类中的toString()方法。
   使得在调用对象的toString()时,返回实体内容信息
 4.自定义类也可以重写toString()方法,当调用此方法时,返回对象的“实体内容”

6. 重写toString方法

//自动生成就行
@Override
	public String toString() {
		return "dog [name=" + name + ", age=" + age + "]";
	}

包装类

1. 单元测试

不用写main方法,直接在方法内测试相关代码

Java中的JUnit单元测试
步骤:
1.选中当前工程 - 右键选择:build path -add libraries - JUnit 4 -下一步
2. 创建Java类:进行单元测试。
此时的Java类要求:①此类是public的 ②此类提供公共的无参的构造器
3. 此类中声明单元测试方法。
此时的单元测试方法:方法的权限是public,没有返回值,没有形参
4. 此单元测试方法上需要声明注释:@Test,并在单元测试中导入:import org.junit.Test;
5. 声明好单元测试方法以后,就可以在方法体内测试相关的代码。
6. 写完代码以后,左键双击单元测试方法名,右键:run as - JUnit Test
说明:
1.如果执行结果没有任何异常:绿条
2.如果执行结果出现异常:红条

2. 基本类型、包装类与String类间的转换

(1)为什么要有包装类(或封装类)

为了使基本数据类型的变量具有类的特征,所以引入了包装类。

(2)基本数据类型与对应的包装类

其中,Byte、Short、Integer、Long、Float、Double的父类是Number。

基本数据类型包装类
byteByte
shortShort
intInteger
longLong
floatFloat
doubleDouble
booleanBoolean
charCharacter
(3)需掌握的:基本数据类型、包装类、String三者之间的相互转换

简易版:
①基本数据类型< - - ->包装类:JDK5.0 新特性:自动装箱与自动拆箱
②基本数据类型、包装类 - - -> String:调用String重载的valueOf(Xxx xxx)
③String - - - >基本数据类型、包装类:调用包装类的parseXxx(String s)

注意:转换时,可能会报NumberFormatException异常

(4)应用场景举例:

①Vector类中关于添加元素,只定义了形参Object类型的方法

v.addElement(Object obj); //基本数据类型--->包装类--->使用多态
(5)相互转换举例

基本数据类型< - - - > 包装类

public void text1() {
		
		 // 自动装箱:基本数据类型--->包装类 
		int num1 = 10 ;
		Integer in1 = num1 ; //自动装箱
		
		boolean b1 = true ;  //自动装箱
		Boolean b2 = b1 ;
		
		// 自动拆箱:包装类--->基本数据类型 
		System.out.println(in1.toString());
		
		int num2 = in1 ;
	}

基本数据类型、包装类 - - - > String

//基本数据类型、包装类--->String:调用String重载的valueOf(Xxx xxx)
		@Test
		public void text2() {
			
			int num1 = 100 ;
			//方式一:连接运算
			String str1 = num1 + ""  ;
			//方式二:调用 String 的 valueOf(Xxx xxx)
			float f1 = 1.23f ;
			String str2 = String .valueOf(f1); // "12.3"
			
			Double d1 = new Double(12.4);
			String str3 = String.valueOf(d1);
			System.out.println(str2);
			System.out.println(str3);
			
		}

String - - - >基本数据类型、包装类

//String--->基本数据类型、包装类:调用包装类的parseXxx(String s)
		@Test
		public void text3() {
				String str1 = "123" ;
				int num1 = Integer.parseInt(str1);
				System.out.println(num1);
				
				String str2 = "true1" ;
				boolean b1 = Boolean.parseBoolean(str2);
				System.out.println(b1);
			}
(6)Integer细节小知识
Integer内部定义了IntegerCache结构,IntegerCache中定义了Integer[],保存了从-128~127范围的整数。
如果我们使用自动装箱的方式,给Integer赋值的范围在-128~127范围时,可以直接使用数组中的元素,不用再去new了。
目的:提高效率
	public void test4(){
		 Integer i = new Integer(1);
		 Integer j = new Integer(1);
		 System.out.println(i == j); //false
		 
		 Integer m = 1 ;
		 Integer n = 1 ;
		 System.out.println(m == n); //true
		  
		 Integer x = 128 ; //相当于new了一个Ingeter对象
		 Integer y = 128 ; //相当于new了一个Ingeter对象
		 System.out.println(x == y); //false 
   	}

static 关键字

1、static:静态的
2、static可以用来修饰:属性、方法、代码块、内部类。
3、使用static修饰属性:静态属性(或类变量)
3.1属性:按是否使用static修饰,又分为:静态属性VS非静态属性(实例变量)

静态变量:我们创建了类的多个对象,多个对象共享同一个静态变量。当通过某一个对象修改静态变量时,会导致其他对象调用此静态变量时,是修改过了的。
实例变量 :我们创建了类的多个对象,每个对象都独立的拥有一套类中的非静态属性。当修改其中一个对象中的非静态属性时,不会导致其他对象中同样的属性值的修改。

3.2 static修饰属性的其他说明:

①静态变量随着类的加载而加载。可以通过"类.静态变量"的方式进行调用
②静态变量的加载要早于对象的创建。
③由于类只会加载一次,则静态变量在内存中也只会存在一份:存在方法区的静态域中。
类可以直接调用类中的类变量,但不能直接调用实例变量;对象可以调用类变量,也可以调用实例变量。

3.3 静态属性举例: System.out; Math.PI; 单例模式 ;
4、 使用static修饰方法:静态方法

①随着类的加载而加载,可以通过“类.静态方法”的方式进行调用
类可以直接调用静态方法,但不能调用非静态方法;对象既可以调用静态方法也可以调用非静态方法。
③ **静态方法中,只能调用静态的方法或属性。**非静态方法中,既可以调用非静态的方法或属性,也可以调用静态的方法或属性。

5、static 注意点:

5.1 在静态的方法内,不能使用this关键字、super关键字
5.2 关于静态属性和静态方法的使用,大家都从生命周期的角度去理解。

6、如何判定属性是否要声明为static的?
  • 属性是可以被多个对象所共享的,不会随着对象的不同而不同。
  • 类中的常量也常常声明为static
7、如何判定方法是否要声明为static的?
  • 操作静态属性的方法,通常设置为static的
  • 工具类中的方法,习惯上声明为static的。此如:math、Arrays、Collections

Static应用举例:单例Singleton设计模式

设计模式

定义:设计模式是在大量的实践中总结和理论化之后优选的代码结构、编程风格、以及解决问题的思考方式。
作用:设计模免去我们自己再思考和摸索。式就像是经典的棋谱,不同的棋局,我们用不同的棋谱,“套路”。

1、类的单例设计模式

要解决的问题:所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例。

并且该类只提供一个取得其对象实例的方法。如果我们要让类在一个虚拟机中只能产生一个对象,我们首先必须将类的构造器的访问权限设置为private,这样,就不能用new操作符在类的外部产生类的对象了,但在类内部仍可以产生该类的对象。因为在类的外部开始还无法得到类的对象,只能调用该类的某个静态方法以返回类内部创建的对象,静态方法只能访问类中的静态成员变量,所以,指向类内部产生的该类对象的变量也必须定义成静态的。
特点
由于单例模式只生成一个实例,减少了系统性能开销,当一个对象的产生需要比较多的资源时,如读取配置、产生其他依赖对象时,则可以通过在应用启动时直接产生一个单例对象,然后永久驻留内存的方式来解决。
例如:java.lang.Runtime 类,便是单例模式 ctrl + shitl + t 输入类名查看源码

2、如何实现单例模式?:饿汉式 VS 懒汉式

单例设计模式的饿汉式

package learning10_21;

public class SingletonTest1 {
	public static void main(String[] args) {
		Bank bank1 = Bank.getInstance();
		Bank bank2 = Bank.getInstance();
		System.out.println(bank1 == bank2);
	}
}

//单例设计模式的饿汉式
class Bank{
	//1.私有化类的构造器
	private Bank() {
		
	}
	//2.内部创建类的对象
	//4.要求对象也必须声明为静态的
	private static Bank instance = new Bank() ;
	//3.提供公共的静态的方法,返回类的对象
	public static Bank getInstance() {
		return instance ;
	}
}

单例模式的懒汉式:

package learning10_21;
//单例设计模式的懒汉式
public class SingletonTest2 {
	public static void main(String[] args) {
		Cat cat1 = Cat.getInstance();
		Cat cat2 = Cat.getInstance();
		System.out.println(cat1 == cat2);
	}
}

class Cat{
	//1.私有化类的构造器
	private Cat(){
		
	}
	//2.声明当前类对象,没有初始化
	//4.此对象也必须声明为static的	
	private static Cat instance = null ;
	//3. 声明public、static 的返回当前类对象的方法
	public static Cat getInstance () {
		if(instance == null) {
			instance = new Cat() ; 
		}
		return instance ;
	}
}

3、区分饿汉式和懒汉式
饿汉式:
弊端:  一上来就将对象创建好,导致对象加载时间过长。
好处: 饿汉式是线程安全的。 
懒汉式:
好处: 延迟对象创建。
当前写法坏处:线程不安全。--->到多线程内容时,再优化写法。
4、单例设计模式应用场景
  • 网站的计数器,一般也是单例模式实现,否则难以同步。
  • 应用程序的日志应用,一般都使用单例模式实现,这一般是由于共享的日志文件一直处于打开状态,因为只能有一个实例去操作,否则内容不好追加。
  • 数据库连接池的设计一般也是采用单例模式, 因为数据库连接是一种数据库资源。
  • 项目中,读取配置文件的类,一般也只有一个对象。没有必要每次使用配置文件数据,都生成一个对象去读取。
  • Application也是单例的典型应用。
  • Windows的Task Manager (任务管理器)就就是很典型的单例模式
  • Windows的RecycleBin(回收站)也也是典型的单例应用。在整个系统运行过程中,回收站一直维护着仅有的一个实例。

理解main方法的语法

main方法的使用说明:
1.main()方法作为程序的入口
2.main()方法也是一个普通的静态方法
3.main()方法可以作为我们与控制台交互的方式。(之前:使用Scanner)

main()方法作为程序的入口.实例:

package learning10_22;
/*
 * main方法的使用说明
 * 1.main()方法作为程序的入口
 * 2.main()方法也是一个普通的静态方法
 * 3.main()方法可以作为我们与控制台交互的方式。(之前:使用Scanner)
 * */
public class manTest {
	public static void main(String[] args) {
		main.main(new String[300]);
	}
}

class main{
	public static void main(String[] args) {
		for(int i=0;i<args.length;i++) {
			args[i] = "args_" + i ;
			System.out.println(args[i]);
		}
	}
}

在这里插入图片描述

main()方法可以作为我们与控制台交互的方式.实例:

package learning10_22;

public class mainDemo {
	public static void main(String[] args) {
		
		for(int i=0;i<args.length;i++) {
			System.out.println("********"+args[i]);
			
			int num = Integer.parseInt(args[i]);
			System.out.println("#####" + num);
		}
	}
}

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

代码块

类的成员之四:代码块(或初始化块)

1.代码块的作用:用来初始化类、对象
2.代码块如果有修饰的话,只能使用static.
3.分类:静态代码块 VS 非静态代码块
3.1.静态代码块:

①内部可以有输出语句。
②随着类的加载而执行,而且只执行一次 (只要类没有重新加载,就不会再次执行)。
③作用:初始化类的信息。
④如果一个类中定义了多个静态代码块,则按照声明的先后顺序执行。
⑤静态代码块的执行要优先于非静态代码块的执行。
⑥静态代码块内只能调用静态的属性、静态的方法,不能调用非静态的结构。

3.2.非静态代码块:

①内部可以有输出语句
②随着对象的创建而执行
③每创建一个对象,就执行一次非静态代码块
④作用:可以在创建对象时,对对象的属性等进行初始化
⑤如果一个类中定义了多个非静态代码块,则按照声明的先后顺序执行
⑥非静态代码块内可以调用静态的属性、静态的方法,或非静态的属性、非静态的方法

package learning10_22;
public class BlockTest {
	public static void main(String[] args) {
		String desc = Person.desc ;
		System.out.println(desc);
		
		Person p1 = new Person();
		Person p2 = new Person();
		Person p3 = new Person();
		System.out.println(p1.age);
	}
}

class Person{
	//属性
	String name;
	int age ;
	static String desc = "I am people" ;
	//构造器
	public Person() {
		
	}
	public Person(String name,int age) {
		this.name = name ;
		this.age = age ;
	}
	//静态代码块
	static{
		System.out.println("hello,static block-1");
		desc = "我是一个爱学习的人-来自于静态类";
	}
	static{
		System.out.println("hello,static block-2");
	}
	//非static代码块
	{
		System.out.println("hello,block");
		age = 1 ;
	}
	//方法
	public void eat() {
		
	}
	@Override
	public String toString() {
		return "Person [name=" + name + ", age=" + age + "]";
	}
	public static void info() {
		System.out.println("I am happy man");
	}
	
}

在这里插入图片描述

4.对属性可以赋值的位置:

①默认初始化
②显示初始化 / ⑤在代码块赋值
③构造器中初始化
④有了对象以后,可以通过"对象.属性"或"对象.方法"的方式。进行赋值

赋值执行的先后顺序: ① - ②/⑤ - ③ - ④

package learning10_22;
/*
 * 对属性可以赋值的位置:
 * ①默认初始化
 * ②显示初始化 /  ⑤在代码块赋值
 * ③构造器中初始化
 * ④有了对象以后,可以通过"对象.属性"或"对象.方法"的方式。进行赋值
 * */
public class OrderTest {
	public static void main(String[] args) {
		Order order = new Order();
		System.out.println(order.OrderId);
	}
}


class Order{
	int OrderId= 3 ;
	{
		OrderId = 4 ;
	}
}

在这里插入图片描述

final 关键字

1.final可以用来修饰的结构:类、方法、变量
2.final用来修饰一个类:此类不能被其它类所继承
比如:String类、System类、StringBuffer类
3. final用来修饰方法:表明此方法不可以被重写
比如:Object类中getClass();
4. final用来修饰变量:此时的"变量"就称为是一个常量
4.1final修饰属性:可以考虑赋值的位置有:显式初始化、代码块中初始化、构造器中初始化。
4.2 final修饰局部变量:尤其是使用final修饰形参时,表明此形参是一个常量 当我们调用此方法时,给常量形参赋一个实参。一旦赋值以后,就只能在方法体内使用此形参,但不能进行重新赋值。

抽象类与抽象方法

1.abstract(关键字):抽象的

2.abstract可以用来修饰的结构:类、方法

3.abstract修饰类:抽象类

  • 此类不能实例化
  • 抽象类中一定有构造器,便于子类实例化时调用(设计:子类对象实例化的全过程)
  • 开发中,都会提供抽象类的子类,让子类对象实例化,完成相关的操作

4.abstract修饰方法:抽象方法

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

5.abstract使用上的注意点:

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

6.抽象类的匿名子类

在这里插入图片描述

7.模板方法设计模式(TemplateMethod) (多态的应用)

抽象类体现的就是一种模板模式的设计,抽象类作为多个子类的通用模板,子类在抽象类的基础上进行扩展、改造,但子类总体上会保留抽象,类的行为方式。

解决的问题:
当功能内部一部分实现是确定的,一部分实现是不确定的。这时可以把不确定的部分暴露出去,让子类去实现。
换句话说,在软件开发中实现一个算法时,整体步骤很固定、通用,这些步骤已经在父类中写好了。但是某些部分易变,易变部分可以抽象出来,供不同子类实现。这就是一种模板模式。

接口interface (关键字)

接口的使用

1.接口使用interface来定义

2.Java中,接口和类是并列的两个结构

3.如何定义接口:定义接口中的成员

3.1 JDK7及以前:只能定义全局常量和抽象方法

全局常量:public static final 的
抽象方法:public abstrsct 的

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

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

5.Java开发中,接口通过让类去实现(implements)的方式来使用.

如果“实现类”覆盖了接口中的所有抽象方法,则此实现类就可以实例化.
如果“实现类”没有覆盖接口中所有的抽象方法,则此实现类仍为一个抽象类.

6.Java类可以实现多个接口 ---->弥补了Java单继承性的局限性

格式:class AA extends BB implements CC,DD,EE…{}

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

package learning10_27;

public class InterfaceTest {
	public static void main(String[] args) {
		plane p1 = new plane();
		p1.fly();
		p1.stop();
		Bullet b1 = new  Bullet() ;
		b1.fly();
		b1.stop();
		b1.attack1();
		b1.method1();
		b1.method2();
	}
}

//接口
interface Fly{
	//全局变量
	public static final int MAX_SPEED = 7900 ; //第一宇宙速度
	/*public static final*/ int MIN_SPEED = 1 ; //最慢速度 (可以省略public static final,但系统会自动加上)
	
	//抽象方法
	public  void fly();
	void stop();
}

//接口
interface Attack{
	public abstract void attack1();
}



//用plane类实现Fly接口
class plane implements Fly{
	@Override
	public void fly() {
		System.out.println("飞机飞飞飞");
	}@Override
	public void stop() {
		System.out.println("飞机刹车刹车刹车");
	}
}

//用kite实现Fly接口,但又没完全实现,还是得声明为abstract类
abstract class kite implements Fly{
	@Override
	public void fly() {
		
	}
}

//用Bullet类实现Fly,Attack两个接口   
class Bullet extends Object implements Fly,Attack,CC{
	@Override
	public void fly() {
		System.out.println("子弹flyflyfly");
	}
	@Override
	public void stop() {
		System.out.println("子弹停停停");
	}
	@Override
	public void attack1() {
	System.out.println("biu~bui~bui~");	
	}
	@Override
	public void method1() {
	System.out.println("CC的方法一");
	}
	@Override
	public void method2() {
		System.out.println("CC的方法二");
	}
}
//*******************************************************************************
// 接口多继承
interface AA{
	public abstract void method1();
}
interface BB{
	public abstract void method2();
}
interface CC extends AA,BB{
	
}

在这里插入图片描述

8. 接口的使用

1.接口使用上也满足多态性
2.接口,实际就是定义了一种规范
3.开发中,体会面向接口编程

从以下代码可以体现出:

package learning10_27;
/*
 * 接口的使用
 * 1.接口使用上也满足多态性
 * 2.接口,实际就是定义了一种规范
 * 3.开发中,体会面向接口编程
 * */
public class USBTest {
	public static void main(String[] args) {
		computer c1 =new computer();
		//1.创建了接口的非匿名实现类的非匿名对象
		Printer p1 = new Printer() ;
		c1.transferDate(p1);
	    //2.创建了接口的非匿名实现类的匿名对象
		c1.transferDate(new Printer());
		//3.创建了接口的匿名实现类的非匿名对象
		Usb phone = new Usb() {
			
			@Override
			public void stop() {
			System.out.println("phne工作");
			}
			@Override
			public void start() {
			System.out.println("phne停止");
			}
		};
		c1.transferDate(phone);
		//4.创建了接口的匿名实现类的匿名对象
		c1.transferDate(new Usb() {
			@Override
			public void start() {
				System.out.println("mp3开始工作");
			}
			@Override
			public void stop() {
				System.out.println("mp3停止工作");
			}
		});
		
	}
}

//电脑类
class computer{
	public void transferDate(Usb u) {
		u.start();
		System.out.println("传输细节");
		u.stop();
	}
}



//USB接口
interface Usb{
	//常量:定义了长、宽、最大最小的传输速度等
	public abstract void  start();
	public abstract void  stop();
}

//Flash类实现USB接口
class Flash implements Usb{
	@Override
	public void start() {
		System.out.println("U盘载入成功");
	}
	@Override
	public void stop() {
		System.out.println("U盘断开连接");
	}
}

//Printer类实现Usb接口
class Printer implements Usb{
	@Override
	public void start() {
		System.out.println("打印机开启");
	}
	@Override
	public void stop() {
		System.out.println("打印机断开");
	}
}

9.接口的应用:代理模式(proxy)

概述:代理模式是Java中开发中使用比较多的一种设计模式。代理设计就是为其他对象提供一种代理以控制对这个对象的访问。

例1 (安全代理:屏蔽对真实角色的直接访问)

package learning10_27;
/*
 * 接口的应用:代理模式
 * */
public class NetWrokTest {
	public static void main(String[] args) {
		Server server = new Server() ;
		ProxyServer proxyServer = new ProxyServer(server) ;
		proxyServer.browse();
	}
}	

//NetWork 接口
interface NetWork{
	public abstract void browse(); //浏览功能
}

//Server类实现NetWork接口 (被代理类)
class Server implements NetWork{
	@Override
	public void browse() {
		System.out.println("真实的服务器访问网络");
	}
}

//ProxyServer类实现NetWork接口 (代理类)
class ProxyServer implements NetWork{
	
	private NetWork work ;
	public ProxyServer(NetWork work) {
		this.work = work ;
	}
	
	public void check() {
		System.out.println("联网之前的检查工作");
	}
	
	
	@Override
	public void browse() {
		check();
		work.browse();	
	}
}

例二 (安全代理:屏蔽对真实角色的直接访问)

package learning10_27;

public class StarTest {
	public static void main(String[] args) {
		agent a = new agent(new Star());
		a.sing();
		a.talk();
		a.money();
	}
}


interface fun {
	public abstract void sing();
	public abstract void talk();
	public abstract void money();
}

class Star implements fun{
	@Override
	public void sing() {
	System.out.println("明星唱歌");
	}
	@Override
	public void talk() {
	System.out.println("谈生意");
	}
	@Override
	public void money() {
	System.out.println("拿钱");
	}
}


class agent implements fun{
	private fun f ;
	//构造器
	public agent(fun f) {
		this.f = f ;
	}
	@Override
	public void talk() {
	System.out.println("经纪人谈生意");
	}
	@Override
	public void money() {
	System.out.println("经纪人拿钱");
	}
	@Override
	public void sing() { //调用明星唱歌
	f.sing();
	}
}

应用场景:

  • 安全代理:屏蔽对真实角色的直接访问。
  • 远程代理:通过代理类处理远程方法调用(RMI)。
  • 延迟加载:先加载轻量级的代理对象,真正需要再加载真实对象比如你要开发一个大文档查看软件,大文档中有大的图片,有可能一个图片有100MB,在打开文件时,不可能将所有的图片都显示出来,这样就可以使用代理模式,当需要查看图片时,用proxy来进行大图片的打开。

分类:

  • 静态代理(静态定义代理类)
  • 动态代理(动态生成代理类)
    JDK自带的动态代理,需要反射等知识

10.接口的应用:工厂模式

核心本质:
实例化对象,用工厂方法代替new操作。
将选择实现类、创建对象统一管理和控制。从而将调用者跟我们的实现类解耦。

11.面试题

在这里插入图片描述

12.JAVA8中接口的新特性

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

知识点1:接口中定义的静态方法,只能通过接口来调用。

知识点2:通过实现类的对象,可以调用接口中的默认方法;如果实现类重写了接口中的默认方法,调用时,仍然调用的是重写以后的方法

知识点3:如果子类(或实现类)继承的父类和实现的接口中声明了同名同参数的方法,那么子类在没有重写此方法的情况下,默认调用的是父类中的同名同参数的方法。–>>>类优先原则

知识点4:如果实现类实现了多个接口,而这多个接口中定义了同名同参数的默认方法,那么在实现类没有重写此方法的情况下,报错。—>接口冲突
这就需要我们必须在实现类中重写此方法

知识点5:如何在子类(或实现类)的方法中调用父类、接口中被重写的方法

package learning10_28;

public class aTest {
	public static void main(String[] args) {
		Sub s1 = new Sub();
//		s1.method1();
		CompareA.method1();
		s1.method2();
		s1.method3();
	}
}


class Sub extends SuperClass implements CompareA,CompareB{
	
	public void method2() {
		System.out.println("覆盖性上海");
	}
	public void method3() {
		System.out.println("深圳");
	}
	public void myMethod() {
		method3();//自己定义的重写方法
		super.method3();//调用的是父类中的方法
		//调用接口中默认方法
		CompareA.super.method3();
		CompareA.super.method2();
	}
}

实例:
当同时实现 Mother 与 Spoony 两个接口,又在Man中想调help方法时,就会报错 (显示两个接口中的方法重名)。
需在Man中重写
继承了Father类中的help方法,从而自动调用父类中的help方法。
③若想在Man中调用接口中的help方法,格式:接口名.super.help();

package learning10_30;

interface Mother{
	default void help() {
		System.out.println("老妈,我来救你了");
	}
}

interface Spoony{
	default void help() {
		System.out.println("媳妇,别怕,我来了");
	}
}

class Father{
	public void help(){
		System.out.println("儿子,救我媳妇");
	}
}

public class Man extends Father implements Mother,Spoony {
	public static void main(String[] args) {
		Man m1 = new Man();
		m1.help();
	}
	
//	public void help() {
		//System.out.println("救谁呢?");
//		Mother.super.help(); //救母亲
		//Spoony.super.help(); //救媳妇
//	}
}

内部类

类的内部成员之五:内部类

1.Java中允许将一个类A声明在另一个类B中,则类A就是内部类,则B称为外部类。

2.内部类的分类:成员内部类(静态非静态) VS 局部内部类(方法内、代码块内、构造器内)
3.成员内部类:
一方面,作为外部类的成员:

  • 调用外部类的成员
  • 可以被static修饰
  • 可以被四种不同的权限修饰

另一方面,作为一个类:

  • 类内可以定义属性、方法、构造器等
  • 可以被final修饰,表示此类不能被继承。不使用final,就可以被继承。
  • 可以被abstract修饰

4.关注如下的三个问题
4.1 如何实例化成员内部类对对象
4.2 如何在成员内部类中区分调用外部类的结构
4.3 开发中局部内部类的使用

在以下代码中可以体现:

package learning10_30;

public class InnerClassTest {
	public static void main(String[] args) {
		//创建Dog实例(静态的成员内部类):
		Person.Dog dog = new Person.Dog() {
			
		};
		dog.show();
		//创建Bird实例(非静态的成员内部类):
//		Person.Bird bird = new Person.Bird() ; 错误写法
		Person p = new  Person();
		Person.Bird bird = p.new Bird(); //内部类属于外部类的成员,所以得先造一个外部类的对象再通过这个对象调用内部类
										//,造内部类的对象。
		bird.sing();
	}

}

class Person{
	String name ;
	int age ;
	public void eat() {
		System.out.println("人,吃饭"); 
	}
	
	//静态成员内部类
	abstract static class Dog{
		String name ;
		int age ;
		public void show() {
			System.out.println("卡拉是条狗");
		}
		
	}
	//非静态成员内部类
	class Bird{
		String name ;
		Bird(){
			
		}
		public void sing() {
			System.out.println("我是一只小小鸟");
			eat(); //省略了 person.this.eat()
			Person.this.eat(); //调用外部类的非静态属性 
		}
		
	}
	
	//方法
	public void method() {
		//局部内部类
		class AA{
			
		}
		//局部内部类
		class BB{
			
		}
	}
	//构造器
	public Person(){
		//局部内部类
		class CC{
			
		}
	}
}

异常处理

异常
.概论
异常: 在Java语言中,将程序执行中发生的不正常情况称为“异常”
(开发过程中的语法错误和逻辑错误不是异常)

●Java程序在执行过程中所发生的异常事件可分为两类:

Error: Java虚拟机无法解决的严重问题。如: JVM系统内部错误、资源耗尽等严重情况。比如: StackOverflowError和OOM。 一般不编写针对性的代码进行处理。

package learning10_30;

public class ErrorTest {
	
	public static void main(String[] args) {
		//1.栈溢出: java.lang.StackOverflowError
		//main(args);
		//2.堆溢出: java.lang.OutOfMemoryError
		Integer[] arr = new Integer[1024*1024*1024];
	}
}

Exception:其它因编程错误或偶然的外在因素导致的一般性问题,可以使用针对性的代码进行处理。例如:

  • 空指针访问
  • 试图读取不存在的文件
  • 网络连接中断
  • 数组角标越界
2.异常体系结构
java.lang.Throwable
	|-----java.lang.Error:一般不编写针对性的代码进行处理。
	|-----java.lang.Exception:可以进行异常的处理
		|---编译时异常(checked)
			|---IOException
			   |---FileNotFoundException 
		    |---ClassNotFoundException
		|---运行时异常(unchecked)
			|---NullPointerException
			|---ArrayIndexOutOfBoundsException
			|---ClassCastException 
			|---NumberFormat Exception
			|---InputMi smatchException
			|---ArithmeticException

3.从程序执行过程,看编译时异常和运行时异常

编译时异常:执行javac. exe命名时,可能出现的异常.
运行时异常:执行java.exe命名时,出现的异常。
在这里插入图片描述

4.面试题:常见的异常都有哪些?举例说明

举例如下:

package learning10_30;

import java.io.File;
import java.io.FileInputStream;
import java.util.Date;
import java.util.Scanner;

import org.junit.Test;

public class ExceptionTest {
	//**************以下为编译时异常************************
	@Test
	public void test7() {
		File file = new File("Hello.txt");
		FileInputStream fis = new FileInputStream(file);
		int data = fis.read();
		while(data != -1) {
			System.out.print((char)data);
			data = fis.read();
			
			fis.close();
		}
	}
	
	//**************以下为运行时异常************************
	//ArithmeticException 算数异常
	/*@Test
	public void test6() {
		int a = 10 ;
		int b = 0 ;
		System.out.println(a/b);
	}*/
	
	//InputMi smatchException  输入不匹配异常
	/*@Test
	public void test5() {
		Scanner scanner =new Scanner(System.in);
		int score = scanner.nextInt();
		System.out.println(score);
		scanner.close(); //关闭资源
	}*/
	
	//NumberFormat Exception 数字格式异常
	/*@Test
	public void test4() {
		String str = "123" ;
		str = "abc" ;
		int num = Integer.parseInt(str);
	}*/
	
	//ClassCastException 类型转换异常
	/*@Test
	public void test3() {
		Object a = new Date() ;
		String str = (String)a ;
	}*/
	
	
	
	//ArrayIndexOutOfBoundsException 
	/*@Test
	public void test2() {
		//ArrayIndexOutOfBoundsException 
//		int[] arr = new int[10];
//		System.out.println(arr[10]);
		
		//StringIndexOutOfBoundsException
		String str = "jklove";
		System.out.println(str.charAt(8));
	}*/
	
	
	//NumberFormat Exception 空指针异常
	/*	@Test
	public void test1() {
		
//		int[] arr =null ;
//		System.out.println(arr[9]);
		
		String str = "jklove";
		str = null ;
		System.out.println(str.charAt(2));
	} */
}

异常处理

1.Java异常处理的抓包模型

过程一:“”:程序正常执行的过程中,一旦出现异常,就会在异常代码处生成一个对应异常类的对象。------并将对象抛出。--------一旦抛出对象以后,其后的代码就不再执行。
关于异常对象的产生:
①系统自动生成的异常对象
②手动的生成一个异常对象,并抛出(throw)

过程二:“”:可以理解为异常的处理方式:①try-catch-finally ② throws

2.异常处理方式一:try-catch-finally
①try-catch-finally的使用
try{
 	//可能出现异常的代码
 }catch(异常类型1 变量名1){
 		//处理异常的方式一
 }catch(异常类型2 变量名2){
  	   //处理异常的方式二
 }catch(异常类型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结构可以嵌套。

②总结:如何看待代码中的编译时异常和运行时异常?

体会1:使用try-catch-finally处理编译时异常,使得程序在编译时就不再报错,但是运行时仍可能报错,相当于我们使用try-catch-finally将一个编译时可能出现的异常,延时到运行时出现。

体会2:体会2:开发中,由于运行时异常比较常见,所以我们通常就不针对运行时异常编写try-catch-finally了。
针对于编译时异常,我们说一定要考虑异常的处理。

③finally的再说明

1.finally是可选的

2.finally中声明的是一定会被执行的代码。即使catch中又出现异常了,try中有return语句,catch中有return语句等情况。

3.像数据库连接、输入输出流、网络编程Socket等资源,JVM是不能自动的回收的,我们需要自己手动的进行资源的释放。此时的资源释放,就需要声明在finally中。

④final、finally 、finalize 三者的区别

没有什么实质性的练习,讲清他们的定义就行。
类似的问题:

throwthrows
collection 和 Collections
StringStringBufferStringBuilder
ArrayListLinkedList
HashMapLinkedHashMap 
重写、重载

结构不相似的:
抽象类、接口
==equals()
sleep()wait()
3.异常处理方式二:throws
①throws
  1. "throws +异常类型"写在方法的声明处。指明此方法执行时,可能会抛出的异常类型。
    一但当方法体执行时,出现异常,仍会在异常代码处生成一个异常类的对象,此对象满足throws后异常类型时,就会被抛出。
    异常代码后续的代码,就不再执行!
②对比两种处理方式
  1. try-catch-finally:真正的将异常给处理掉了。
  2. throws的方式只是将异常抛给了方法的调用者。并没有真正将异常处理掉。
③开发中如何选择两种处理方式
  1. 如果父类中被重写的方法没有throws方式处理异常,则子类重写的方法也不能使用throws,意味着如果子类重写的方法中有异常,必须使用try- catch-finally方式处理。

  2. 执行的方法a中,先后又调用了另外的几个方法,这几个方法是递进关系执行的。我们建议这几个方法使用throw的方式进行处理。而执行的方法a可以考虑使用try-catch- finally方式进行处理。

④注意点

方法重写的规则之一:
子类重写的方法拋出的异常类型不大于父类被重写的方法拋出的异常类型

4.手动抛出异常:throw
①throw

在程序执行中,除了自动抛出异常对象的情况之外,我们还可以手动的throw一个异常类的对象。

②throw和throws的区别:

throw表示抛出一个异常类的对象,生成异常对象的过程。声明在方法体内。
throws 属于异常处理的一种方式,声明在方法的声明处。

③代码例:
package learning11_1;

public class StudentTest {
	public static void main(String[] args) {
		try {
			Student s = new Student();
			s.regist(-1001);
			System.out.println(s);
		} catch (Exception e) {
//			e.printStackTrace();
			System.out.println(e.getMessage());
		}
	}
	
}


class Student{
	int id ;
	public void regist(int id) throws Exception {
		if(id>0) {
			this.id = id ;
		}else {
//			System.out.println("输入数据非法!");
			//手动抛出异常对象
//			throw new RuntimeException("输入数据非法!");
			throw new Exception("数据非法");
		}
	}
	@Override
	public String toString() {
		return "Student [id=" + id + "]";
	}

}
5.用户自定义异常类

如何自定义异常类?

  1. 继承于现有的异常结构:RuntimeException、Exception
  2. 提供全局常量:serialVersionUID
  3. 提供重载的构造器
package learning11_1;

public class MyException extends RuntimeException{
	
	static final long serialVersionUID = -7034897190745766939L;
	
	public MyException() {
		
	}
	public MyException(String msg) {
		super(msg);
	}
}
6. 异常练习

编写应用程序EcmDef.java,接收命令行的两个参数,要求不能输入负数,计算两数相除。
对数据类型不一致(NumberFormatException)、缺少命令行参数(ArrayIndexOutOfBoundsException、
除0(ArithmeticException)及输入负数(EcDef自定义的异常)进行异常处理。

提示:
(1)在主类(EcmDef)中定义异常方法(ecm)完成两数相除功能。
(2)在main()方法中使用异常处理语句进行异常处理。
(3)在程序中,自定义对应输入负数的异常类(EcDef)。
(4)运行时接受参数java EcmDef 20 10 //args[0]=“20" args[1]=“10”
(5)Interger类的static方法parselnt(String s)将s转换成对应的int值。
如: int a=Interger.parselnt(“314"); //a=314;

package learning11_1;

public class EcmDef {
	public static void main(String[] args) throws EcDef{
		try {
			int i = Integer.parseInt(args[0]);
			int j = Integer.parseInt(args[1]);
			int result = ecm(i, j);
			System.out.println(result);
		}catch(NumberFormatException e) {
			System.out.println("数据类型不一致");
		}catch(ArrayIndexOutOfBoundsException e) {
			System.out.println("缺少命令行参数");
		}catch(ArithmeticException e) {
			System.out.println("除0");
		}catch(EcDef e) {
			System.out.println(e.getMessage());
		}
	}
	
	public static int ecm (int i ,int j) throws EcDef  {
		if(i<0||j<0) {
			throw new EcDef("分子或分母为负数了!");
		}
		return i / j ;	
	}
}

项目三

IDEA

Ctrl + Shift + t 搜索关键字看源码

下载地址:

https://www.jetbrains.com/idea/download/#section=windows
IDEA分为两个版本:旗舰版(Ultimate)和社区版(Community)。
旗舰版收费(限30天免费试用),社区版免费,这和Eclipse很大区别。

常用配置的设置
快捷键的设置

Alt + Nnt

模板的使用

多线程

程序、进程、线程

程序(program)是为完成特定任务、用某种语言编写的一组指令的集合。即指一段静态的代码,静态对象。

进程(process)是程序的一次执行过程,或是正在运行的一个程序。是一个动态的过程:有它自身的产生、存在和消亡的过程。——生命周期

  • 如:运行中的QQ,运行中的MP3播放器
  • 程序是静态的,进程是动态的
  • 进程作为资源分配的单位,系统在运行时会为每个进程分配不同的内存区域

线程(thread),进程可进一步细化为线程,是一个程序内部的一条执行路 径。

  • 若一个进程同一时间并行执行多个线程,就是支持多线程的
  • 线程作为调度和执行的单位,每个线程拥有独立的运行栈和程序计数器(pc),线程切换的开销小
  • 一个进程中的多个线程共享相同的内存单元/内存地址空间>>它们从同一堆中分配对象,可以访问相同的变量和对象。这就使得线程间通信更简便、高效。但多个线程操作共享的系统资源可能就会带来安全的隐患。

一个Java应用程序java.exe, 其实至少有三个线程: main()主线程, gc()垃圾回收线程,异常处理线程。当然如果发生异常,会影响主线程。

并行:多个CPU同时执行多个任务。比如:多个人同时做不同的事。

并发:一个CPU(采用时间片)同时执行多个任务。比如:多个人做同一件事。

线程的创建与使用

多线程的创建方式一:继承于Thread类

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

例:

package Package01;

public class ThreadTest {
    public static void main(String[] args) {
        MyThread t1 = new MyThread();

        t1.start();
        //问题一:我们不能通过直接带调用run()的方式启动线程。
//        t1.run();

        //问题二:再启动一个线程,遍历100以内的偶数。不可以还让已经start()的线程去执行。会报IllegalThreadStateException异常
//        t1.start();
        //我们需要重新创建一个线程的对象
        MyThread t2 = new MyThread();
        t2.start();


        //如下操作仍然是在main线程中执行的
        System.out.println("hello");
        for (int i = 0; i < 100; i++) {
            if (i % 2 == 0){
                System.out.println(Thread.currentThread().getName()+ ":"+ i+"*************maim()********");
            }
        }
    }
}

class MyThread extends Thread{
    @Override
    public void run() {
            for (int i = 0; i < 100; i++) {
                if (i % 2 == 0){
                    System.out.println(Thread.currentThread().getName()+ ":"+ i);
                }
        }
    }
}
练习(创建两个线程 / 创建Thread类的匿名子类)

创建两个分线程,其中一个线程遍历100以内的偶数,另一个线程遍历100以内的奇数

package Package.exer;
public class ThreadDemo {
    public static void main(String[] args) {
//        MyThread t1 = new MyThread();
//        t1.start();
//        MyThread t2 = new MyThread();
//        t2.start();
        //如果只用一次,可以考虑使用匿名的方式  创建Thread类的匿名子类的方式
            new Thread(){
                @Override
                public void run() {
                    for (int i = 0; i < 100; i++) {
                        if (i%2!=0){
                            System.out.println(Thread.currentThread().getName()+":"+ i);
                        }
                    }
                }
            }.start();

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

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

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

    }
}
线程的常用方法
  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(): 判断当前线程是否存活

例:

package Package.java;
public class ThreadMethonTest {
    public static void main(String[] args) {
        HelloThread h1 = new HelloThread("Thread:101");
//        h1.setName("线程一");
        h1.start();
        //给主线程命名
        Thread.currentThread().setName("主线程");
        for (int i = 0; i < 100; i++) {
            if (i%2==0){
                System.out.println(Thread.currentThread().getName()+":"+i);
            }

            if (i==20){
                try {
                    h1.join();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
        System.out.println(h1.isAlive());
    }
}

class HelloThread extends Thread{
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            if (i%2==0){
                try {
                    sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+":"+i);
            }

            if (i%20==0){
                yield();
            }
        }
    }
    public HelloThread(String name){
        super(name);
    }
}
线程优先级

1
MAX_PRIORITY:10
MIN_PRIORITY:1
NORM_PRIORITY:5 -->默认优先级

2.如何获取和设置当前线程的优先级:
getPriority():获取线程的优先级
setPriority(int p):设置线程的优先级

说明:高优先级的线程要抢占低优先级线程cpu的执行权。但是只是从概率上讲,高优先级的线程高概率的情况下被执行。并不意味着只有当高优先级的线程执行完以后,低优先级的线程才执行。

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

例子:

package November11_3;
public class WindowTest1 {
    public static void main(String[] args) {
        Windows1 windows1 = new Windows1();
        Thread t1 = new Thread(windows1);
        t1.setName("一号窗口:");
        t1.start();
        Thread t2 =new Thread(windows1);
        t2.setName("二号窗口:");
        t2.start();
        Thread t3 = new Thread(windows1);
        t3.setName("三号窗口:");
        t3.start();
    }
}

class Windows1 implements Runnable{
    private int ticket = 100 ;
    @Override
    public void run() {
        while (true){
            if (ticket>0){
                System.out.println(Thread.currentThread().getName()+"卖票咯,还剩票号"+ticket);
                ticket--;
            }else {
                break;
            }
        }
    }
}

比较创建线程的两种方式

开发中:优先选择:实现Runnable接 口的方式
原因:
1.实现的方式没有类的单继承性的局限性
2.实现的方式更适合来处理多个线程有共享数据的情况。

联系: public class Thread implements Runnable
相同点:两种方式都需要重写run(),将线程要执行的逻辑声明在run()中。

线程的生命周期

在这里插入图片描述

线程的同步

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

方式一:同步代码块
 synchronized(同步监视器){
 *              //  需要被同步的代码
 *             //   1.操作共享数据的代码,即为需要被同步的代码 -->不能包含代码多了, 也不能包含代码少了。
 *            //    2.共享数据:多个线程共同操作的变量。   比如:ticket就是共享数据。
 *           //     3.同步监视器,俗称:锁。 任何一个类的对象,都可以充当锁。
 *              //      要求:多个线程必须要共用同一把锁。
 *             //       补充:在实现Runnable接口创建多线程的方式中,我们可以考虑使用this充当同步监视器。
 *          }
方式二:同步方法

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

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

使用同步机制将单例模式中的懒汉式改写为线程安全的
package November11_3;
public class BankTest {

}

class Bank{
    private Bank(){ }

    private static Bank instance = null ;

    public static Bank getInstance(){
        //方式一:效率稍差
//        synchronized (Bank.class) {
//            if (instance == null){
//                instance = new Bank();
//            }
//            return instance;
//        }
        //方式二:效率更高
        if (instance == null){
            synchronized (Bank.class) {
                if (instance == null){
                    instance = new Bank();
                }
            }
        }
        return instance;
    }
}
线程的死锁问题

死锁
不同的线程分别占用对方需要的同步资源不放弃,都在等待对方放弃自己需要的同步资源,就形成了线程的死锁。
出现死锁后,不会出现异常,不会出现提示,只是所有的线程都处于阻塞状态,无法继续。
解决方法
专门的算法、原则
尽量减少同步资源的定义
尽量避免嵌套同步

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

线程的通讯

线程通讯的例子:使用两个线程打印 1-100.线程1,线程2 交替打印

涉及到的三个方法:

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

说明:

  1. wait(),notify(),notifyAll()三个方法必须使用同步代码块或同步方法中。
  2. wait(),notify(),notifyAll()三个方法的调用者必须是同步代码块或同步方法中的同步监视器。
    否则,会出现IlLegaLMonitorStateException异常
  3. wait(),notify(),notifyAll()三个方法是定于在java.lang.Object类中的。
class Number implements Runnable{
    private int number =1 ;
    @Override
    public void run() {
        while (true){

            synchronized (this) {

                notifyAll();

                if (number<=100){

                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                    System.out.println(Thread.currentThread().getName()+":"+number);
                    number++;

                    try {
                        //使得调用如下wait()方法的线程进入阻塞状态
                        wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                }else {
                    break;
                }
            }

        }
    }
}

public class CommunicationTest {
    public static void main(String[] args) {
        Number n1 = new Number();
        Thread t1 = new Thread(n1);
        Thread t2 = new Thread(n1);
        t1.setName("线程一");
        t2.setName("线程二");
        t1.start();
        t2.start();
    }
}

sleep()和wait() 的异同?

1.相同点:一旦执行方法,都可以使得当前的线程进入阻塞状态。
2.不同点:
(1)两个方法声明的位置不同:Thread类中声明sleep(),Object类中声明wait()。
(2)调用的要求不同: sleep( )可以在任何需要的场景下调用。wait( )必须使用在同步代码块或同步方法中
(3)关于是否释放同步监视器:如果两个方法都使用在同步代码块或同步方法中,sleep()不会释放锁,wait()释放锁。

JDK5.0新增线程创建方式

新增方式一:实现Callable接口
与使用Runnable相比,Callable功能更强大些

  • call()可以有返回值
  • call()可以抛出异常,被外面的操作捕获,获取异常的信息
  • Callable支持泛型的返回值

新增方式二:使用线程池

好处:
1.提高响应速度(减少了创建新线程的时间)
2.降低资源消耗(重复利用线程池中线程,不需要每次都创建)
3.便于线程管理

corePoolSize:核心池的大小
maximumPoolSize:最大线程数
keepAliveTime:线程没有任务时最多保持多长时间后会终止

面试题:创建多线程有几种方式? 答:四种!

常用类

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

  1. String声明为final的,不可被继承

  2. String实现了Serializable接口:表示字符事是支持序列化的。
    实现了Comparable接口:表示String可以比较大小

  3. String内部定义了final char[] value用于存储字符串数据

  4. String:代表不可变的字符序列。简称:不可变性。
    体现: ①当对字符串重新赋值时,需要重写指定内存区域赋值,不能使用原有的value进行赋值
    ②当对现有的字符串进行连接操作时,也需要重新指定内存区域赋值,不能使用原有的value进行赋值。
    ③当调用String 的replace()方法修改指定字符或字符串时,也需要重新指定内存区域赋值,不能使用原有的value进行赋值。

  5. 通过字面量的方式(区别于new)给一个字符串赋值,此时的字符串值声明在字符串常量池中。

  6. 字符串常量池中是不会存储相同内容的字符串的。

String的实例化方式

方式一:通过字面量定义的方式
方式二:通过new +枸造器的方式

通过字面量定义的方式:此时的s1和s2的数据javaEE声明在方法区中的字符串常量池中。

String s1 = "javaEE" ;
String s2 =  "javaEE" ;

通过new +构造器的方式:此时的s3和s4保存的地址值,是数据在堆空间中开辟空间以后对应的地址值。

String s3 = new  String("javaEE");
String s4 = new String("javaEE");
System.out.println(s1 == s2); //true
System.out.println(s1 == s3); //fasle
System.out.println(s1 == s4); //fasle
System.out.println(s3 == s4); //fasle

面试题: String s = new String(“abc”);方式创建对象,在内存中创建了几个对象?
答:两个 ,一个是堆空间中new结构,另一个是char[]对应的常量池中的数据: “abc”

String不同拼接操作的对比

在这里插入图片描述
结论:

  1. 常量与常量的拼接结果在常量池。且常量池中不会存在相同内容的常量。
  2. 只要其中有一个是变量,结果就在
  3. 如果拼接的结果调用intern()方法,返回值就在常量池中
String常用方法

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

String与基本数据类型转换

String --> 基本数据类型 调用parseint(String s)方法

@Test
    public void test1(){
        String s1 = "666" ;
        int i1 = Integer.parseInt(s1);
        System.out.println(i1);

        String s2 = String.valueOf(i1);
        String s3 = s2 + "";

    }

基本数据类型 --> String 调用String类的 valueOf(int n) 方法
或者 + “”

String 与 字符数组转换

String 与 char[] 之间的转换

String --> char[]: 调用String的toCharArray()
char[] --> String: 调用String的构造器

@Test
    public void test2(){
        String str1 = "abc123" ;
        char[] c1 = str1.toCharArray();  //String类的 toCharArray()方法可将字符串变为字符数组
        for (int i = 0; i < c1.length; i++) {
            System.out.println(c1[i]);

        }

        char[] c2 = new char[]{'Y','u','Z','a','i'};
        String str2 = new String(c2);
        System.out.println(str2);
    }
String 与 byte[ ] 之间的转换

编码 String --> byte[]: 调用String的getBytes() ; 字符串 --> 字节
解码 byte --> String ; 字节 --> 字符串
解码时,解码集与编码集需要一致,不然会出现乱码。

@Test
    public void test3() throws UnsupportedEncodingException {
        String str1 = "yuzai123" ;
        byte[] b1 = str1.getBytes();
        System.out.println(Arrays.toString(b1)); // 将数组转换成String类型输出, 若直接输出b1,输出的是地址值

        byte[] gbk1 = str1.getBytes("gbk"); // 使用gbk字符集进行编码
        System.out.println(Arrays.toString(gbk1));

        System.out.println("***************** ");
        String str2 = new String(b1);
        System.out.println(str2);

        String str3 = new String(gbk1);
        System.out.println(str3);
    }
String、StringBuffer、 StringBuilder三者的异同?

String: 不可变的字符序列 ;底层使用char[]存储

StringBuffer: 可变的字符序列;线程安全的,效率低;底层使用char[]存储

StringBuilder: 可变的字符序列 ;jdk5.0新增的,线程不安全的,效率高;底层使用char[]存储

StringBuffer 常用方法

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

总结:增删改查插长度遍历

对比String、StringBuffer、StringBuilder三者的效率:

从高到低排列:StringBuilder > StringBuffer > String

JDK8之前日期时间API
1. java.lang.System类
 @Test
    public void test1(){
        long time = System.currentTimeMillis(); //返回当前时间1970年1月1日0时日分日秒之间以毫秒为单位的时间差。(称为时间戳)
        System.out.println(time);
    }
2. java.util.Date类

java.util.Date类
–构造器1 Date() 创建一个对应当前时间的Date对象
–构造器2 Date(long date) 创建指定毫秒数的Date对象
方法1:toString() 显示当前的年月日时分秒
方法2: getTime() 获取当前Date对象对应的毫秒数 (时间戳)

Date time2 = new Date(1937306108410L);
        System.out.println(time2.toString());

        //java.sql.Date类
        java.sql.Date date1 = new  java.sql.Date(12387484514874l);     // 创建java.sql.Date对象
        System.out.println(date1);  //2362-07-18

             // 将java.util.Date对象转换为java.sql.Date 情况一
        Date date2 = new java.sql.Date(455874184544l);
        java.sql.Date date3 = (java.sql.Date) date2 ;  // 将date2强转为java.sql.Date类 (多态)

            // 将java.util.Date对象转换为java.sql.Date 情况二
        Date time0 = new Date(123456789L);
        java.sql.Date date4 = new java.sql.Date(time0.getTime()) ;  //  (利用时间戳的共性)
java.sql.Date类

java.sql.Date类 对应着数据库中的日期类型的变量
> 如何实例化
> java.util.Date对象转换为java.sql.Date 对象

//java.sql.Date类
        java.sql.Date date1 = new  java.sql.Date(12387484514874l);     // 创建java.sql.Date对象
        System.out.println(date1);  //2362-07-18

             // 将java.util.Date对象转换为java.sql.Date 情况一
        Date date2 = new java.sql.Date(455874184544l);
        java.sql.Date date3 = (java.sql.Date) date2 ;  // 将date2强转为java.sql.Date类 (多态)

            // 将java.util.Date对象转换为java.sql.Date 情况二
        Date time0 = new Date(123456789L);
        java.sql.Date date4 = new java.sql.Date(time0.getTime()) ;  //  (利用时间戳的共性)

复习

P43 041.尚硅谷_Java基本语法-复习1 12:09
P44 042.尚硅谷_Java基本语法-复习2 17:44
P68 066.尚硅谷_Java基本语法-每天一考 19:15
P69 067.尚硅谷_Java基本语法-复习1 09:24
P70 068.尚硅谷_Java基本语法-复习2:变量的定义 13:29
P71 069.尚硅谷_Java基本语法-复习3:变量的运算规则 12:49
P90 088.尚硅谷_Java基本语法-每天一考16:52
P91 089.尚硅谷_Java基本语法-复习1:算术与比较运算符11:04
P92 090.尚硅谷_Java基本语法-复习2:比较与逻辑运算符13:08
P93 091.尚硅谷_Java基本语法-复习3:位运算符与三元运算符 19:21

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值