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规定的4种权限(从小到大排列):private->缺省->protected->public
②4种权限都可以用来修饰类及类的内部结构:属性、方法、构造器、内部类
③修饰类的话,只能使用: 缺省、public。
Java提供了四种权限修饰符来修饰类及类的内部结构,体现类及类的内部结构在被调用时可见性的大小。
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);
}
}
①默认初始化
②显示初始化
③构造器赋值
④通过“对象.方法” 或 “对象.属性” 的方式。赋值
以上操作的先后顺序:① - ② - ③ - ④
JavaBean 是一种Java语言写成的可重用组件。
所谓JavaBean,是指符合如下标准的Java类:
①类是公共的
②有一个无参的公共的构造器
③有属性,且有对应的get、set方法
用户可以使用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 可以用来修饰:属性、方法、构造器
this理解为:当前对象 或 当前正在创建的对象
2.1 在类的方法中,我们可以使用“this.属性” 或 “this.方法” 的方法,调用当前对象属性或方法。但是通常情况下,我们都选择省略“this.”。特殊情况下,如果方法的形参和类的属性同名时,我们必须显示的使用“this.变量”的方式,表明此变量是属性,而非形参。
2.2 在类的构造器中,我们可以使用“this.属性” 或 “this.方法” 的方法,调用当前正在创建的对象属性或方法。但是通常情况下,我们都选择省略“this.”。特殊情况下,如果方法的形参和类的属性同名时,我们必须显示的使用“this.变量”的方式,表明此变量是属性,而非形参。
①:我们在类的构造器中,可以显示的使用 “this(形参列表 )”
方式,调用本类中指定的其他构造器
②:构造器中不能通过 “this(形参列表)”
方式调用自己
③:如果一个类中有n个构造器,则最多有n-1 个构造器使用了 “this(形参列表 )”
,剩下的那一个是super()。
④:规定: “this(形参列表 )”
必须声明在当前构造器的首行
⑤:构造器内部,最多只能声明一个 “this(形参列表 )”
,用来调用其他的构造器
①构造器的作用是什么?使用中有哪些注意事项?
②关于类的属性的赋值,有几种赋值的方式。以及赋值的先后顺序
③this 关键字可以用来调用哪些结构
④java中目前学习涉及到的四种权限修饰符都有什么?并说各自的权限范围
①减少代码冗余,提高了代码的复用性。
②便于功能的扩展
③为后续的多态性的使用,提供了前提
calss A extends B{}
# A为子类,B为父类
2.1 体现:一旦子类A继承父类B以后,子类A就获取了父类B中声明的所有的属性和方法。
特别的,父类中声明为private的属性或方法,子类继承父类后,仍然认为获取了父类中私有的结构。但因为封装性的影响,使得子类不能直接调用父类的结构。
2.2 子类继承父类以后,还可以声明自己特有的属性和方法:实现功能的拓展
1.一个类可以被多个子类继承
2.Java中的类是单继承性:一个类只能有一个父类 (接口是多继承性)
3.子父类是相对的概念。
4.子类直接继承的父类,称为:直接父类。间接继承的父类成为:间接父类
5.子类继承父类以后,就获取了直接父类以及所有父类中声明的属性和方法
1.如果我们没有显示的声明一个类的父类的话,则此类继承于java.lang.Objeck类。
2.所有的java类(除java.lang.Objeck类之外)都直接或间接的继承于java.lang.Objeck类
3.意味着,所有的java类具有java.lang.Objeck类声明的功能。
① 设置断点
设置方法:(对“行数”双击)
注:可设置多个断点
②右键-> debug as -> java application->yes
③常用操作
操作 | 作用 |
---|---|
step into 跳入(f5) | 进入当前行所调用的方法中 |
step over 跳过(f6) | 执行完当前行的语句,进入下一行 |
step return 跳回(f7) | 执行完当前行所在的方法,进入下一行 |
drop to frame | 回到当前行所在方法的第一行 |
resume 恢复 | 执行完当前所在断点的所有代码,进入下一个断点,如果没有就结束 |
方法的重写(override / overwrite)
方法的声明: 权限修饰符 返回值类型 方法名(形参列表) throws 异常的类型 {
//方法体
}
约定俗称:子类中的叫重写方法,父类中的叫被重写的方法
①子类重写的方法的方法名和形参列表与父类被重写的方法的方法名和形参列表相同
②子类重写的方法的权限修饰符不小于父类被重写的方法的权限修饰符
③返回值类型
a.父类被重写的方法的返回值类型是void,则子类重写的方法的返回值类型只能是void
b.父类被重写的方法的返回值类型是A类型,则子类重写的方法的返回值类型可以是A类或A类的子类
c.父类被重写的方法的返回值类型是基本数据类型(比如:double),则子类重写的方法的返回值类型必须是相同的
④子类重写的方法抛出的异常类型不大于父类被重写的方法抛出的异常类型
子类和父类中的同名同参数的方法要么都声明为非static的(考虑重写),要么都声明为static的(不是重写)。
3.1 我们可以在子类的方法或构造器中。通过使用“super.属性”或“属性.方法”的方式,显示的调用父类中声明的属性或方法。但是,通常情况下,我们习惯省略"super."
3.2 特殊情况:当子类和父类中定义了同名的属性时,我们要想在子类中调用父类中声明的属性,则必须显示的使用“super.属性”的方式,表明调用的是父类中的属性。
3.3 特殊情况:当子类重写了父类中的方法以后,我们想在子类的方法中调用父类中被重写的方法时,则必须显示的使用"super.方法"的方式,表明调用的是父类中被重写的方法。
4.1 我们可以在子类的构造器中显示的使用“super(形参列表)”的方式,调用父类中声明的指定的构造器
4.2 "super(形参列表)"的使用,必须声明在子类构造器的首行!
4.3 我们在类的构造器中,针对于"this(形参列表)"或"super(形参列表)“只能二选一,不能同时出现
4.4 在构造器的首行,没有显示的声明"this(形参列表)“或"super(新参列表)”,则默认调用的是父类中空参的构造器:super()
4.5 在类的多个构造器中,至少有一个类的构造器中使用了"super(形参列表)”,调用父类的构造器
子类继承父类以后,就获取了父类中声明的属性或方法。
创建子类的对象,在堆空间中,就会加载所有父类中声明的属性。
当我们通过子类的构造器创建子类对象时,我们一定会直接或间接的调用其父类的的构造器,进而调用父类的父类的构造器,直到调用了Java.lang.Object类中空参的构造器为止。正因为加载过所有的父类的结构,所以才可以看到内存中有父类中的结构,子类对象才可以考虑进行调用。
需要明确的是:虽然创建子类对象时,调用了父类的构造器,但是至始至终只创建过一个对象,那个对象就是 新new出来的子类对象。
可以理解为一个事物的多种形态。
对象的多态性:父类的引用指向子类的对象 (或子类的对象赋给父类的引用)
举例:
person p = new Man();
Object obj = new Date();
虚拟方法调用:
有了对象的多态性以后,我们在编译期,只能调用父类声明的方法,但在运行期,我们实际执行的是子类重写父类的方法。
总结:编译,看左边;运行,看右边。
Person e = new Person();
e.getlnfo();
Studente = new Student();
e.getlnfo();
子类中定义了与父类同名同参数的方法,在多态情况下,将此时父类的方法称为虚拟方法,父类根据赋给它的不同子类对象,动态调用属于子类的该方法。这样的方法调用在编译期是无法确定的。
Person e = new Student();
e.getInfo(); //调用Student类的getInfo()方法
编译时e为Person类型,而方法的调用是在运行时确定的,所以调用的是Student类的getInfo(方法。——动态绑定
①类的继承关系
②方法的重写
举例一:
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();
}
}
对象的多态性,只适用于方法,不适用于属性(编译和运行都看左边)
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("喵!喵!喵!");
}
}
/*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();//⑤问
}
}
//20、20、true、10、20
1.二者的定义细节,略
2.从编译和运行的角度看:
重载,是指允许存在多个同名方法,而这些方法的参数不同。编译器根据方法不同的参数表,对同名方法的名称做修饰。对于编译器而言,这些同名方法就成了不同的方法。它们的调用地址在编译期就绑定了。Java的重载是可以包括父类和子类的,即子类可以重载父类的同名不同参数的方法。所以:对于重载而言,在方法调用之前,编译器就已经确定了所要调用的方法,这称为“早绑定”或“静态绑定”;
而对于多态,只有等到方法调用的那一刻,解释运行器才会确定所要调用的具体方法,这称为“晚绑定”或“动态绑定”。
引用一句Bruce Eckel的话:“不要犯傻, 如果它不是晚绑定,它就不是多态。”
答:
①二者的概念
②重载和重写的具体规则
③重载:不表现为多态性
重写:表现为多态性
重载,是指允许存在多个同名方法,而这些方法的参数不同。编译器根据方法不同的参数表,对同名方法的名称做修饰。对于编译器而言,这些同名方法就成了不同的方法。它们的调用地址在编译期就绑定了。Java的重载是可以包括父类和子类的,即子类可以重载父类的同名不同参数的方法。所以:对于重载而言,在方法调用之前,编译器就已经确定了所要调用的方法,这称为“早绑定”或“静态绑定”;
而对于多态,只有等到方法调用的那一刻,解释运行器才会确定所要调用的具体方法,这称为“晚绑定”或“动态绑定”。
引用一句Bruce Eckel的话:“不要犯傻, 如果它不是晚绑定,它就不是多态。”
例: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);
}
}
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) 可以使用在基本数据类型变量和引用数据类型变量中
(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()进行重写
① ==既可以比较基本类型也可以比较引用类型。对于基本类型就是比较值,对于引用类型就是比较内存地址
②equals方法,它是属于java.lang.Object类里面的方法,如果该方法没有被重写过默认也是==;而String等类的equals方法是被重写过的,而且String类在日常开发中用的比较多,便形成了equals是比较值的错误观点
③具体要看自定义类里有没有重写Object的equals方法来判断
④通常情况下,重写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 自动生成
①对称性:如果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”。
Object类中toString()的使用:
1.当我们输出一个对象的引用时,实际上就是调用当前对象的toString()
2.Object类中toString的定义:
public String toString(){
return getClass().getName() + "@" +Integer.toHexString(hashCode());
}
3.像String、Date、File、包装类等都重写了Object类中的toString()方法。
使得在调用对象的toString()时,返回实体内容信息
4.自定义类也可以重写toString()方法,当调用此方法时,返回对象的“实体内容”
//自动生成就行
@Override
public String toString() {
return "dog [name=" + name + ", age=" + age + "]";
}
不用写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.如果执行结果出现异常:红条
为了使基本数据类型的变量具有类的特征,所以引入了包装类。
其中,Byte、Short、Integer、Long、Float、Double的父类是Number。
基本数据类型 | 包装类 |
---|---|
byte | Byte |
short | Short |
int | Integer |
long | Long |
float | Float |
double | Double |
boolean | Boolean |
char | Character |
简易版:
①基本数据类型< - - ->包装类:JDK5.0 新特性:自动装箱与自动拆箱
②基本数据类型、包装类 - - -> String:调用String重载的valueOf(Xxx xxx)
③String - - - >基本数据类型、包装类:调用包装类的parseXxx(String s)
注意:转换时,可能会报NumberFormatException异常
①Vector类中关于添加元素,只定义了形参Object类型的方法
v.addElement(Object obj); //基本数据类型--->包装类--->使用多态
基本数据类型< - - - > 包装类
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);
}
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
}
静态变量:我们创建了类的多个对象,多个对象共享同一个静态变量。当通过某一个对象修改静态变量时,会导致其他对象调用此静态变量时,是修改过了的。
实例变量 :我们创建了类的多个对象,每个对象都独立的拥有一套类中的非静态属性。当修改其中一个对象中的非静态属性时,不会导致其他对象中同样的属性值的修改。
①静态变量随着类的加载而加载。可以通过"类.静态变量"的方式进行调用
②静态变量的加载要早于对象的创建。
③由于类只会加载一次,则静态变量在内存中也只会存在一份:存在方法区的静态域中。
④类可以直接调用类中的类变量,但不能直接调用实例变量;对象可以调用类变量,也可以调用实例变量。
①随着类的加载而加载,可以通过“类.静态方法”的方式进行调用
②类可以直接调用静态方法,但不能调用非静态方法;对象既可以调用静态方法也可以调用非静态方法。
③ **静态方法中,只能调用静态的方法或属性。**非静态方法中,既可以调用非静态的方法或属性,也可以调用静态的方法或属性。
5.1 在静态的方法内,不能使用this关键字、super关键字
5.2 关于静态属性和静态方法的使用,大家都从生命周期的角度去理解。
定义:设计模式是在大量的实践中总结和理论化之后优选的代码结构、编程风格、以及解决问题的思考方式。
作用:设计模免去我们自己再思考和摸索。式就像是经典的棋谱,不同的棋局,我们用不同的棋谱,“套路”。
要解决的问题:所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例。
并且该类只提供一个取得其对象实例的方法。如果我们要让类在一个虚拟机中只能产生一个对象,我们首先必须将类的构造器的访问权限设置为private,这样,就不能用new操作符在类的外部产生类的对象了,但在类内部仍可以产生该类的对象。因为在类的外部开始还无法得到类的对象,只能调用该类的某个静态方法以返回类内部创建的对象,静态方法只能访问类中的静态成员变量,所以,指向类内部产生的该类对象的变量也必须定义成静态的。
特点:
由于单例模式只生成一个实例,减少了系统性能开销,当一个对象的产生需要比较多的资源时,如读取配置、产生其他依赖对象时,则可以通过在应用启动时直接产生一个单例对象,然后永久驻留内存的方式来解决。
例如:java.lang.Runtime 类,便是单例模式 ctrl + shitl + t
输入类名查看源码
单例设计模式的饿汉式
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 ;
}
}
饿汉式:
弊端: 一上来就将对象创建好,导致对象加载时间过长。
好处: 饿汉式是线程安全的。
懒汉式:
好处: 延迟对象创建。
当前写法坏处:线程不安全。--->到多线程内容时,再优化写法。
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);
}
}
}
类的成员之四:代码块(或初始化块)
①内部可以有输出语句。
②随着类的加载而执行,而且只执行一次 (只要类没有重新加载,就不会再次执行)。
③作用:初始化类的信息。
④如果一个类中定义了多个静态代码块,则按照声明的先后顺序执行。
⑤静态代码块的执行要优先于非静态代码块的执行。
⑥静态代码块内只能调用静态的属性、静态的方法,不能调用非静态的结构。
①内部可以有输出语句
②随着对象的创建而执行
③每创建一个对象,就执行一次非静态代码块
④作用:可以在创建对象时,对对象的属性等进行初始化
⑤如果一个类中定义了多个非静态代码块,则按照声明的先后顺序执行
⑥非静态代码块内可以调用静态的属性、静态的方法,或非静态的属性、非静态的方法
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");
}
}
①默认初始化
②显示初始化 / ⑤在代码块赋值
③构造器中初始化
④有了对象以后,可以通过"对象.属性"或"对象.方法"的方式。进行赋值
赋值执行的先后顺序: ① - ②/⑤ - ③ - ④
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 ;
}
}
1.final可以用来修饰的结构:类、方法、变量
2.final用来修饰一个类:此类不能被其它类所继承。
比如:String类、System类、StringBuffer类
3. final用来修饰方法:表明此方法不可以被重写
比如:Object类中getClass();
4. final用来修饰变量:此时的"变量"就称为是一个常量
4.1final修饰属性:可以考虑赋值的位置有:显式初始化、代码块中初始化、构造器中初始化。
4.2 final修饰局部变量:尤其是使用final修饰形参时,表明此形参是一个常量 当我们调用此方法时,给常量形参赋一个实参。一旦赋值以后,就只能在方法体内使用此形参,但不能进行重新赋值。
1.abstract 不能用来修饰:属性、构造器等结构
2.abstract不能用来修饰私有方法、静态方法、final的方法、final的类
抽象类体现的就是一种模板模式的设计,抽象类作为多个子类的通用模板,子类在抽象类的基础上进行扩展、改造,但子类总体上会保留抽象,类的行为方式。
解决的问题:
当功能内部一部分实现是确定的,一部分实现是不确定的。这时可以把不确定的部分暴露出去,让子类去实现。
换句话说,在软件开发中实现一个算法时,整体步骤很固定、通用,这些步骤已经在父类中写好了。但是某些部分易变,易变部分可以抽象出来,供不同子类实现。这就是一种模板模式。
接口的使用
全局常量:public static final 的
抽象方法:public abstrsct 的
如果“实现类”覆盖了接口中的所有抽象方法,则此实现类就可以实例化.
如果“实现类”没有覆盖接口中所有的抽象方法,则此实现类仍为一个抽象类.
格式:class AA extends BB implements CC,DD,EE…{}
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{
}
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("打印机断开");
}
}
概述:代理模式是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();
}
}
应用场景:
分类:
核心本质:
实例化对象,用工厂方法代替new操作。
将选择实现类、创建对象统一管理和控制。从而将调用者跟我们的实现类解耦。
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.成员内部类:
一方面,作为外部类的成员:
另一方面,作为一个类:
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:其它因编程错误或偶然的外在因素导致的一般性问题,可以使用针对性的代码进行处理。例如:
java.lang.Throwable
|-----java.lang.Error:一般不编写针对性的代码进行处理。
|-----java.lang.Exception:可以进行异常的处理
|---编译时异常(checked)
|---IOException
|---FileNotFoundException
|---ClassNotFoundException
|---运行时异常(unchecked)
|---NullPointerException
|---ArrayIndexOutOfBoundsException
|---ClassCastException
|---NumberFormat Exception
|---InputMi smatchException
|---ArithmeticException
编译时异常:执行javac. exe命名时,可能出现的异常.
运行时异常:执行java.exe命名时,出现的异常。
举例如下:
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));
} */
}
异常处理
过程一:“抛”:程序正常执行的过程中,一旦出现异常,就会在异常代码处生成一个对应异常类的对象。------并将对象抛出。--------一旦抛出对象以后,其后的代码就不再执行。
关于异常对象的产生:
①系统自动生成的异常对象
②手动的生成一个异常对象,并抛出(throw)
过程二:“抓”:可以理解为异常的处理方式:①try-catch-finally ② throws
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了。
针对于编译时异常,我们说一定要考虑异常的处理。
1.finally是可选的
2.finally中声明的是一定会被执行的代码。即使catch中又出现异常了,try中有return语句,catch中有return语句等情况。
3.像数据库连接、输入输出流、网络编程Socket等资源,JVM是不能自动的回收的,我们需要自己手动的进行资源的释放。此时的资源释放,就需要声明在finally中。
没有什么实质性的练习,讲清他们的定义就行。
类似的问题:
throw 和 throws
collection 和 Collections
String、StringBuffer、StringBuilder
ArrayList、LinkedList
HashMap、LinkedHashMap
重写、重载
结构不相似的:
抽象类、接口
== 、 equals()
sleep()、wait()
如果父类中被重写的方法没有throws方式处理异常,则子类重写的方法也不能使用throws,意味着如果子类重写的方法中有异常,必须使用try- catch-finally方式处理。
执行的方法a中,先后又调用了另外的几个方法,这几个方法是递进关系执行的。我们建议这几个方法使用throw的方式进行处理。而执行的方法a可以考虑使用try-catch- finally方式进行处理。
方法重写的规则之一:
子类重写的方法拋出的异常类型不大于父类被重写的方法拋出的异常类型
在程序执行中,除了自动抛出异常对象的情况之外,我们还可以手动的throw一个异常类的对象。
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 + "]";
}
}
如何自定义异常类?
package learning11_1;
public class MyException extends RuntimeException{
static final long serialVersionUID = -7034897190745766939L;
public MyException() {
}
public MyException(String msg) {
super(msg);
}
}
编写应用程序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 ;
}
}
Ctrl + Shift + t 搜索关键字看源码
https://www.jetbrains.com/idea/download/#section=windows
IDEA分为两个版本:旗舰版(Ultimate)和社区版(Community)。
旗舰版收费(限30天免费试用),社区版免费,这和Eclipse很大区别。
Alt + Nnt
程序(program)是为完成特定任务、用某种语言编写的一组指令的集合。即指一段静态的代码,静态对象。
进程(process)是程序的一次执行过程,或是正在运行的一个程序。是一个动态的过程:有它自身的产生、存在和消亡的过程。——生命周期
线程(thread),进程可进一步细化为线程,是一个程序内部的一条执行路 径。
一个Java应用程序java.exe, 其实至少有三个线程: main()主线程, gc()垃圾回收线程,异常处理线程。当然如果发生异常,会影响主线程。
并行:多个CPU同时执行多个任务。比如:多个人同时做不同的事。
并发:一个CPU(采用时间片)同时执行多个任务。比如:多个人做同一件事。
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);
}
}
}
}
创建两个分线程,其中一个线程遍历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);
}
}
}
}
例:
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的执行权。但是只是从概率上讲,高优先级的线程高概率的情况下被执行。并不意味着只有当高优先级的线程执行完以后,低优先级的线程才执行。
例子:
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;
}
}
死锁
不同的线程分别占用对方需要的同步资源不放弃,都在等待对方放弃自己需要的同步资源,就形成了线程的死锁。
出现死锁后,不会出现异常,不会出现提示,只是所有的线程都处于阻塞状态,无法继续。
解决方法
专门的算法、原则
尽量减少同步资源的定义
尽量避免嵌套同步
线程通讯的例子:使用两个线程打印 1-100.线程1,线程2 交替打印
涉及到的三个方法:
说明:
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();
}
}
1.相同点:一旦执行方法,都可以使得当前的线程进入阻塞状态。
2.不同点:
(1)两个方法声明的位置不同:Thread类中声明sleep(),Object类中声明wait()。
(2)调用的要求不同: sleep( )可以在任何需要的场景下调用。wait( )必须使用在同步代码块或同步方法中
(3)关于是否释放同步监视器:如果两个方法都使用在同步代码块或同步方法中,sleep()不会释放锁,wait()释放锁。
新增方式一:实现Callable接口
与使用Runnable相比,Callable功能更强大些
好处:
1.提高响应速度(减少了创建新线程的时间)
2.降低资源消耗(重复利用线程池中线程,不需要每次都创建)
3.便于线程管理
corePoolSize:核心池的大小
maximumPoolSize:最大线程数
keepAliveTime:线程没有任务时最多保持多长时间后会终止
面试题:创建多线程有几种方式? 答:四种!
String:字符串,使用一对" "引起来表示。
String声明为final的,不可被继承
String实现了Serializable接口:表示字符事是支持序列化的。
实现了Comparable接口:表示String可以比较大小
String内部定义了final char[] value用于存储字符串数据
String:代表不可变的字符序列。简称:不可变性。
体现: ①当对字符串重新赋值时,需要重写指定内存区域赋值,不能使用原有的value进行赋值
②当对现有的字符串进行连接操作时,也需要重新指定内存区域赋值,不能使用原有的value进行赋值。
③当调用String 的replace()方法修改指定字符或字符串时,也需要重新指定内存区域赋值,不能使用原有的value进行赋值。
通过字面量的方式(区别于new)给一个字符串赋值,此时的字符串值声明在字符串常量池中。
字符串常量池中是不会存储相同内容的字符串的。
方式一:通过字面量定义的方式
方式二:通过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 --> 基本数据类型 调用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 与 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的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: 不可变的字符序列 ;底层使用char[]存储
StringBuffer: 可变的字符序列;线程安全的,效率低;底层使用char[]存储
StringBuilder: 可变的字符序列 ;jdk5.0新增的,线程不安全的,效率高;底层使用char[]存储
总结:增删改查插长度遍历
从高到低排列:StringBuilder > StringBuffer > String
@Test
public void test1(){
long time = System.currentTimeMillis(); //返回当前时间1970年1月1日0时日分日秒之间以毫秒为单位的时间差。(称为时间戳)
System.out.println(time);
}
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.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()) ; // (利用时间戳的共性)