面向对象
面向对象(上)
两个要素
- 类:对一类事物的描述,是抽象的、概念上的定义
- 对象:是实际存在的该类事物的每个个体,也成为实例(instance)
面向对象程序设计的重点是类的设计
设计类就是设计类的成员
- 属性 = 成员变量 = field = 字段
- 方法 = 成员方法 = 函数 = method
- 创建类的对象 = 创建实例 = 类的实例化
类和对象的使用(三步)
- 创建类,设计类的成员
- 创建类的对象
- 通过“对象.属性”或“对象.方法”调用对象结构
内存解析
- 堆(Heap):存放对象实例
- 栈(Stack):存储局部变量
- 方法区(Method Area):存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的编码等数据
类中属性的使用
-
属性(成员变量) 和 局部变量
-
相同点
定义变量的格式 : 数据类型 变量名 = 变量值
先声明后使用
变量都有其对应的作用域
- 不同点
1.在类中声明的位置不同
属性: 直接定义在类的一对大括号内
局部变量: 声明在方法内、方法形参、代码块内、构造器形参、构造器内部的变量
2.关于权限修饰符的不同
属性: 可以在声明属性时,指明其权限,使用权限修饰符
常用的权限修饰符:private、public、缺省、protected
局部变量:不可以使用权限修饰符
3.默认初始化值不同
属性:类的属性,根据其类型都有默认初始化值
整型(byte、short、int、long) 0
浮点型(float、double) 0.0
字符型(char) 0(或'\u0000')
布尔型(boolean) false
引用数据类型(类、数组、借口) null
局部变量:没有默认初始化值
所以,我们调用局部变量之前,一定要显式赋值
特别的,形参调用时,我们赋值即可。
4.在内存中加载的位置不同:
属性:加载到堆空间中(非static)
局部变量:加载到栈空间
万事万物皆对象
- 在Java语言范畴中,我们都将功能结构等封装到类中,通过类的实例化来调用具体的功能结构
Scanner、String等
文件:File
网络资源:URL
- 涉及到Java语言与前端HTML、后端数据库交互时,前后端的结构在Java层面交互时,都体现为类、对象
Java内存解析
内存解析的说明
- 引用类型的变量只可能存储两类值, null 或者 地址值(含变量的类型)
匿名对象的使用
- 我们创建的对象没有显式地赋给一个变量名为匿名对象
- 特征:匿名对象只能调用一次
- 使用如下
package cn.sxu.wmy;
public class InstanceTest {
public static void main(String[] args) {
Phone p = new Phone();
System.out.println(p);
// p.sendEmail();
// p.playGame();
// new Phone().sendEmail();
// new Phone().price = 1999;
// new Phone().showPrice();
//匿名对象的使用
PhoneMall mall = new PhoneMall();
mall.show(new Phone()); //匿名对象这么用可以多次执行
}
}
class PhoneMall{
public void show(Phone phone) {
phone.playGame();
phone.sendEmail();
}
}
class Phone{
double price;
public void sendEmail() {
System.out.println("发邮件");
}
public void playGame() {
System.out.println("玩游戏");
}
public void showPrice() {
System.out.println("手机的价格是:" + price);
}
}
方法的重载(overload)
定义
在同一个类中,允许存在一个以上的同名方法,只要他们参数个数和参数类型不同即可。
“两同一不同” 相同方法名
参数列表不同、参数方法不同、参数类型不同
举例
Arrays中的重载sort()
判断是否是重载
跟方法的权限修饰符、返回值类型、形参变量名、方法体都没有关系!
- 在通过对象调用方法时,如何确定某一个指定的方法
方法名 -------> 参数列表
public class OverLoadTest {
public static void main(String[] args) {
OverLoadTest test = new OverLoadTest();
test.getSum(1,2);
}
//如下四个方法构成重载
public void getSum(int i , int j) {
System.out.println(i + j);
}
public void getSum(double i, double j) {
System.out.println(i + j);
}
public void getSum(String i, double j) {
System.out.println(i + j);
}
}
可变个数形参的方法
- jdk5.0新增的内容
- 可变个数形参的格式: 数据类型 ... 变量名
- 当调用可变个数形参方法时,传入的参数个数可以是0个、1个、2个
public class MethodArgsTest {
public static void main(String[] args) {
MethodArgsTest test = new MethodArgsTest();
//test.show(1);
test.show();
test.show("hello", "world");
}
// public void show(int i ) {
//
// }
// public void show(String strs) {
//
// }
public void show(String ... strs) {
}
}
关于变量的赋值
-
如果数据是基本数据类型,此时赋值的是变量
所保存的数据值
-
如果数据是引用数据类型,此时赋值的是变量所保存的数据的地址值
方法的形参的传递机制
值传递
形参:方法定义时,声明的小括号内的参数
实参:方法调用时,实际传递给形参的数据
值传递机制
如果参数是基本数据类型,此时实参赋给形参的是实参真实存储的数据值
如果参数是引用数据类型,此时实参赋给形参的是实参存储数据的地址值
方法的参数传递(妙妙题)
特征:封装与隐藏
问题的引入
封装性的体现
-
将类的属性私有化(private),同时提供公共的方法(public)来获取(getXXX)和设置(setXXX)此属性的值
-
不对外暴露的私有方法
-
单例模式......
封装性的体现需要权限修饰符来配合
- Java规定的四种权限:private、default、protected、public
- 四种权限可以修饰类和类的内部结构:属性、方法、内部类、构造结构
总结封装性
- Java提供了四种修饰符来修饰类及类的内部结构体现类及类的内部结构在被调用时的可见性大小
构造器(或构造方法、constructor)的使用
作用
-
创建对象给对象进行初始化
Order o = new Order(); Person p = new Person("Peter", 15);
说明
-
如果没有显式地定义类的构造器的话,则默认系统提供一个空参的构造器
-
定义构造器的格式:权限修饰符 类名(形参列表){}
-
创建类的对象: new + 构造器
-
一个类中定义的多个构造器,彼此构成重载
-
一旦我们显式地定义了类的构造器后,系统就不再提供默认的空参构造器
-
一个类中至少有一个构造器
特征
- 具有与类相同的名称
- 不声明返回值类型
- 不能被static、final、synchronized、abstra、native修饰,不能有return语句返回值
//构造器
public Person(){ // Person p = new Person();
System.out.println("Person().."); // p.age = 15;
}
public Person(String n) { // Person p1 = new Person("zhangsan");
name = n;
}
属性赋值的先后顺序
1> 默认初始化
2> 显式初始化
3> 构造器中初始化
4> 通过“对象.方法” 或"对象.属性"的方式赋值
最终还是看方式4
JavaBean的使用
JavaBean是一种Java语言写成的可重用组件
JavaBean是指符合如下标准的Java类:
类是公共的
有一个无参的公共的构造器
有属性,且有对应的get、set方法
public class Customer {
private int Id;
private String name;
public Customer() {
}
public void setId(int id) {
Id = id;
}
public int getId() {
return Id;
}
public void setName(String n) {
name = n;
}
public String getName() {
return name;
}
}
this关键字的使用
this可以用来修饰属性、方法、构造器
- this修饰属性和方法:this理解为当前对象
- 在类的方法中我们可以使用"this.属性"或"this.方法"的方式调用当前对象属性或方法,但是通常情况下我们都选择省略"this."。特殊情况下,如果方法的形参和类的属性同名时,我们必须显式使用"this.变量"的方式表明此变量是属性而非形参
- 构造器同理
public Person(String name, int age){
this.name = name;
this.age = age;
}
this调用构造器
- 我们在类的构造器中可以显式的使用"this(形参列表)"方式,调用本类中指定的其他构造器
- 构造器中不能通过"this(形参列表)"调用自己
- 构造器内部最多之只能声明一个"this(形参列表)"调用其他构造器
//如果调用空参构造器,可以直接在另一构造器中加入:this();
UML类图
package、import关键字
package
- 为了更好实现项目中类的管理,提供了包的概念
- 使用package声明类或者接口
- 包属于标识符,遵循标识符的命名规则、规范(xxxyyyzzz)、见名知意
- 每“.”一次就代表一层文件目录
- 同一个包下不能命名同一个接口、类,不同包下可以。
import
-
import导入
-
在源文件中显式的使用import导入指定包下的类、接口
-
声明在包的声明和类的声明之间
-
如果需要导入多个结构,则并列写出即可
-
可以使用xxx.*的方式,导入xxx包下的所有结构
-
import java.util.Arrays; String str = Arrays.toString(new int[]{1,2,3});
-
如果使用的类或接口是java.lang包下定义的,则可以省略import结构
-
如果使用的类或接口是本包下定义的,则可以省略import结构
-
如果在源文件中使用了不同包下的同名的类,则必须至少有同一个类需要以全类名的方式显示
-
Account acct = new Account(1000); //全类名方式显示 cn.sxu.wmyexer.Account acct1 = new cn.sxu.wmyexer.Account(100,20,2000);
-
使用"xxx.*"方式表明可以调用xxx包下的所有结构,但是如果使用的是xxx子包下的结构,则仍需要显式导入
import.static导入指定类或接口中的静态结构
MVC设计模式
面向对象(中)
面向对象特征之二:继承性
- 减少了代码的冗余,提高了代码的复用性
- 便于功能的扩展
- 为之后多态性的使用提供了前提
继承性的格式 class A extends B
- 子类、派生类、subclass
- 父类、超类、基类、superclass
代码层面体现
1.一旦子类A继承父类B后,子类A就获取了父类B中声明的结构、属性、方法
(私有属性、方法也获取到了但是由于封装性不可以直接调用)
2.子类继承父类后还可以声明自己特有的属性和方法,实现功能的拓展
extends 延展 拓展
Java中关于继承性的规定
1.一个类可以被多个子类继承
2.Java中类的单继承性:一个子类只能有一个父类
3.子父类是相对的概念(有多层继承关系)
4.子类直接继承的父类成为直接父类,简介继承的父类称为间接父类
5.子类继承父类以后就获取了直接父类以及所有间接父类中声明的属性、方法
eclipse 的 一个快捷键
eclipse 的 一个快捷键 : 光标放在类名上按下Ctrl + T ---------->>>>>>>> 可以显示该类的继承性关系
Object类
1.如果我们没有显式地声明一个类的父类的话,则此类继承于java.lang.Object类
2.所有的Java类都直接或间接地继承于java.lang.Object类
3.所有的Java类具有java.lang.Object类声明的功能
Object类中的功能
Object类中的属性、方法具有通用性
equals(Object obj) 、toString()
== 和 equals() 的区别
1、== 可以使用在基本数据类型和引用数据类型变量中
如果比较的是基本数据类型,可以比较两个变量保存的数据是否相等。(不一定类型要相同)
如果比较的是引用数据类型,可以比较两个对象的地址值是否相同,即两个引用是否指向同一个对象实体
2、equals()是一个方法不是运算符
只适用于引用数据类型
Object类中equals()的定义
Object类中的equals()方法和==的引用数据类型比较是相同的
String 、Date、File、包装类中都重写了Object 类中的equals()方法,重写以后比较的不是两个引用的地址是否相同,而是比较两个对象的实体内容是否相同
通常情况下,我们自定义的类如果使用equals()的话,也通常是比较两个对象的实体内容是否相同。那么我们就需要对Object类中的equals()进行重写
package cn.sxu.wmyexer8;
public class Customer {
String name;
int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Customer(String name, int age) {
super();
this.name = name;
this.age = age;
}
public Customer() {
super();
}
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj instanceof Customer) {
Customer cust = (Customer)obj;//需要向下转型才可以调用子类属性
if (this.age == cust.age && this.name.equals(cust.name)) {
return true;
}else {
return false;
}
}
return false;
}
}
public static void main(String[] args) {
Customer cust1 = new Customer("Tom", 21);
Customer cust2 = new Customer("Tom", 21);
System.out.println(cust1.equals(cust2));
}
toString()的使用
1、当我们输出一个对象的引用时,实际上就是调用当前对象的toString()
2、Object类中toString()的定义(最开始输出地址值的原因)
3、像String、Date、File、包装类等都重写了Object类中的toString()方法,使得调用对象的toString()方法时,返回实体内容信息
debug调试
如何调试程序
-
- System.out.println().
-
- Eclipse - Debug调试
方法的重写(overread/overwrite)
1.重写:子类继承父类后可以对父类中同名同参数的方法进行覆盖操作
2.应用:重写以后,当创建子类对象以后,通过子类对象调用子父类中的同名同参数的方法时,实际执行的是子类重写的父类的 方法。
重写的规定
方法的声明:
权限修饰符 返回值类型 方法名(形参列表) throws 异常的类型{ 方法体
}
约定俗称
子类中的叫重写的方法,父类中的叫被重写的方法
1.子类重写的方法的方法名和形参列表与父类被重写的方法的方法名和形参列表相同
2.子类重写的方法的权限修饰符不小于父类被重写的方法的权限修饰符
特殊情况:子类中不能重写父类中声明为private权限的方法
3.返回值类型:
父类被重写的方法返回值类型是void,则子类重写的方法的返回值类型只能是void
父类被重写的方法返回值类型是A类型,则子类重写的方法的返回值类型可以是A类或A类的子类
父类被重写的方法返回值类型是基本数据类型(比如是:double),则子类重写的方法的返回值类型必须是相同的基本数据类型(必须也是double)
4.子类重写的方法抛出的异常类型不大于父类被重写的方法抛出的异常类型
5.子类和父类中的同名同参数的方法要不都声明为非static(非静态)的(考虑重写),要不都声明为static(静态)的(不是重写)
6.方法改写所需遵从的throws规则:父类方法所抛出的异常范围要能够包含子类方法所抛出的异常范围。
四种访问权限修饰符
对于class的权限修饰符只可以用public和default(缺省)
1.public类可以在任意地方被访问
2.default类只可以被同一个包内部的类访问
同一个包中的其他类,不可以调用Order类中私有的属性、方法
在不同包的子类中,不能调用Order类中声明为private和缺省权限的属性、方法
不同包下的普通类(非子类)要使用Order类,不可以调用声明为private、default(缺省)、protected权限的属性、方法
super关键字
1.super理解为:父类的
2.super可以用来调用属性、方法、构造器
3.super的使用
3.1我们可以在子类的方法或构造器中,通过使用"super.属性"或"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.何为多态性:对象的多态性:父类的引用指向子类的对象
3.多态的使用:虚拟方法调用
有了对象的多态性以后,我们在编译期,只能调用父类中声明的方法,但在运行期,我们执行的是子类重写父类的方法。
总结:编译看左边,运行看右边。
4.多态性的使用前提:①类的继承关系 ②方法的重写
编译是父类中的方法,运行时是子类重写的方法
不能调用子类所特有的方法
5.对象的多态性,只适用于方法,不适用于属性(编译运行都看左边)
多态是编译时行为还是运行时行为?
运行时行为。
虚拟方法调用(多态情况下)
子类中定义了与父类同名同参数的方法,在多态情况下,将此时父类的方法称为虚拟方法,父类根据赋给他的不同子类对象,动态调用属于子类的该方法。这样的方法调用在编译器是无法确定的。
//正常调用
Person e = new Person();
e.getInfo();
Student e = new Student();
e.getInfo();
//虚拟方法调用
Person e = new Student();
e.getInfo();//调用Student类的getInfo()方法
有了对象的多态性以后
Person p2 = new Man();
内存中实际上是加载了子类特有的属性和方法的,但是由于变量声明为父类类型,导致编译时,只能调用父类中声明的属性和方法。子类特有的属性和方法不能调用。
那么如何才能调用子类特有的属性和方法?
向下转型:使用强制类型转换符。
Man m1 = (Man) p2;
基本数据类型转换
较低级 转为 较高级:自动类型提升(int 转为 double)
较高级 转为 较低级:强制类型转换(用小括号)
风险:精度损失
在多态性中
向上转型:多态
向下转型:使用instanceof进行判断
风险:可能转换不成功
//使用强转时,可能出现ClassCastException的异常。
Woman w1 = (Woman) p2;
instanceof操作符
x instanceof A : 检验x 是否为 类 A的对象, 返回值为 boolean型s
- 要求x所属的类与类A必须是子类与父类的关系,否则编译错误
- 如果x属于类A的子类B,x instanceof A值也为true
使用情境:为了避免在向下转型时出现ClassCastException的异常,我们在向下转型之前,先进行instanceof的判断,一旦返回true,就进行向下转型。如果返回false,不进行向下转型。
if(p2 instanceof Woman){
Woman w1 = (Woman)p2;
w1.goShopping();
System.out.println("******Woman******");
}
if(p2 instanceof Man){
Man m2 = (Man)p2;
m2.earnMoney();
System.out.println("******Man******");
}
如果 a instanceof A返回true,则 a instanceof B也返回true. 其中,类B是类A的父类。
if(p2 instanceof Person){
System.out.println("******Person******");
}
易错:
//练习:
//问题一:编译时通过,运行时不通过
//举例一:
Person p3 = new Woman();
Man m3 = (Man)p3;
//举例二:
Person p4 = new Person();
Man m4 = (Man)p4;
//问题二:编译通过,运行时也通过
Object obj = new Woman();
Person p = (Person)obj;
//问题三:编译不通过
Man m5 = new Woman();
String str = new Date();
Object o = new Date();
String str1 = (String)o;
1.若子类重写了父类方法,就意味着子类里定义的方法彻底覆盖了父类里的同名方法,系统将不可能把父类里的方法转移到子类中。编译看左边,运行看右边
2.对于实例变量则不存在这样的现象,即使子类里定义了与父类完全相同的实例变量,这个实例变量依然不可能覆盖父类中定义的实例变量编译运行都看左边
package cn.sxu.wmyexer6;
class Base {//父类
int count = 10;
public void display() {
System.out.println(this.count);
}
}
class Sub extends Base {
int count = 20;
public void display() {
System.out.println(this.count);
}
}
public class FieldMethodTest {
public static void main(String[] args){
Sub s = new Sub();
System.out.println(s.count);//属性不继承输出20
s.display();//20
Base b = s;//赋地址值 多态性
System.out.println(b == s);//true
System.out.println(b.count);//属性编译运行都看左边 Base 类 10
b.display();//实际调用的是子类方法
}
}
区分方法的重写和重载
1.二者的概念
2.重载和重写的具体概念
3.重载:不表现为多态性
重写:表现为多态性
可变个数形参
1.格式:对于方法的形参,数据类型 ... 形参名
2.可变个数的形参的方法与同名的方法之间构成重载
3.可变个数的形参方法在调用时,可以有从0到多个参数
4.使用可变个数形参的方法与方法的形参是数组的使用方法一致。一致到认为方法③和⑤是同一个方法的地步,但是方法三是可变个数形参,能包括方法①和②,①②注释掉则自动调用③
5.若一个方法中存在可变个数形参,则可变个数形参一定要声明在最后如:方法④
6.在一个方法中最多声明一个可变个数形参
13 public class TestKeBian {
14 public static void main(String[] args) {
15 TestKeBian kb=new TestKeBian();
16 kb.sayHello();
17 System.out.println();
18 kb.sayHello("@@@");
19 System.out.println();
20 //kb.sayHello(new String[] {"###","$$$","%%%"});//用于数组声明方法的调用
21 kb.sayHello("###","$$$","%%%");//可用于可变形参方法的调用,不可用于数组声明方法
22
23 System.out.println("\n\n");//换行
24 System.out.println(kb.getSum(1, 5));
25 System.out.println(kb.getSum(1, 3, 5));
26 System.out.println(kb.getSum(1,2,3,4,5,6));
27 }
28 //以下的四个方法之间构成重载
29 /* public void sayHello(){//方法①
30 System.out.println("Hello World!");
31 }
32 public void sayHello(String str){//方法②
33 System.out.println("Hello"+str);
34 }*/
35 //定义可变个数形参String...args
36 //这种声明方法虽与以上两个方法重载,但也包括以上两个方法
37 //若以上两个方法注释掉,则自动调用此方法来完成输出操作
38 public void sayHello(String...args){//方法③
39 System.out.println("调用了可变个数形参:");
40 for(int i=0;i<args.length;i++){
41 System.out.print(args[i]+"\t");
42 }
43 }
44 public void sayHello(int t,String...args){//方法④
45 System.out.println(t);
46 for(int i=0;i<args.length;i++){
47 System.out.print(args[i]+"\t");
48 }
49 }
50 // public void sayHello(String[] args){//方法⑤
51 // for(int i=0;i<args.length;i++){
52 // System.out.print(args[i]+"\t");
53 // }
54 // }
多态一道笔试题
package cn.sxu.wmyexer8;
//考查多态的笔试题目:
public class InterviewTest1 {
public static void main(String[] args) {
Base base = new Sub();
base.add(1, 2, 3);
Sub s = (Sub)base;
s.add(1,2,3);
}
}
class Base {
public void add(int a, int... arr) {
System.out.println("base");
}
}
class Sub extends Base {
public void add(int a, int[] arr) {
System.out.println("sub_1");
}
public void add(int a, int b, int c) {
System.out.println("sub_2");
}
}
Java中的JUnit单元测试
/*
* 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.如果执行结果出现异常:红条
*/
现在是JUnit5...好像和视频里教程不太一样...用到了再查吧。
包装类(Wrapper)的使用
针对八种基本数据类型定义相应的引用类型—包装类(封装类)
有了类的特点,就可以调用类中的方法,Java才是真正的面向对象
基本数据类型 -- >包装类
//基本数据类型 -- >包装类, 调用包装类的构造器
public void test1() {
int num1 = 10;
// System.out.println(num1.toString());
Integer in1 = new Integer(num1);
System.out.println(in1.toString());
//包装类-->基本数据类型,调用包装类的xxxValue()
public void test2() {
Integer in1 = new Integer(12);
int i1 = in1.intValue();
System.out.println(i1 + 1);
Float f1 = new Float(12.3);
float f2 = f1.floatValue();
System.out.println(f2 + 1);
}
//JDK5.0新特性 自动装箱与自动拆箱
public void test4() {
int num2 = 10;
Integer in2 = num2;
//自动拆箱
int num3 = in1;
//基本、包装类 --> String类型调用String重载的ValueOf(xxx xxx)
int num2 = 10;
//方式1:连接运算
String str1 = num1 + "";
//方式二:调用
float f1 = 12.3f;
String str2 = String.valueOf(f1);
Double d1 = new Double(12.3);
String str3 = String.valueOf(d1);
}
public void test5() {
//String -- > 包装类、基本 调用包装类的parsexxx()
String str1 = "123";
int num1 = Integer.parseInt(str1);
System.out.println(num1 + 2);
}
面向对象(下)
static关键字的使用
1、static:静态的
2、static可以用来修饰:属性、方法、代码块、内部类
3、修饰属性:静态变量(或叫类变量)
属性:按是否使用static修饰,又分为:静态属性、非静态属性(实例变量)
实例变量:我们创建了类的多个对象,每个对象都独立的拥有一套类中的非静态属性,当修改一个对象的非静态属性时,不会导致其他对象同样的属性的修改
静态变量:我们创建了类的多个对象,多个对象共享同一个静态表变量。当通过某一个对象修改静态变量时,会导致其他对象调用此静态变量时,是修改过的
stitac修饰属性的其他说明
静态变量随着类的加载而加载,可以通过"类.静态变量"的方式调用
静态变量的加载遭遇对象的创建
类只加载一次,静态变量在内存中也只会存在一份,存在方法去的静态域中。
静态属性举例:System.out; Math.PI;
类变量和实例变量内存解析
4、修饰方法
随着类的加载而加载,可以通过"类.静态方法"的方式调用
静态方法中只能调用静态方法或属性
非静态方法中,既可以调用非静态方法或属性,也可以调用静态方法或属性。
5、注意
静态方法内不能使用this关键字,super关键字
6、
开发中如何确定一个属性是否要声明为static?
属性是可以被多个对象所共享的,不会随着对象的不同而不同
操作静态属性的方法,通常设置为static
工具类的方法,习惯上声明为static
比如:Math 、 Arrays 、 Collections
单例
所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对 某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法。 如果我们要让类在一个虚拟机中只能产生一个对象,我们首先必须将类的构 造器的访问权限设置为private,这样,就不能用new操作符在类的外部产生 类的对象了,但在类内部仍可以产生该类的对象。因为在类的外部开始还无 法得到类的对象,只能调用该类的某个静态方法以返回类内部创建的对象, 静态方法只能访问类中的静态成员变量,所以,指向类内部产生的该类对象 的变量也必须定义成静态的。
package sxu.wmy02;
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;
}
}
//懒汉式
class Order{
//1.私有化类的构造器
private Order(){
}
//2.声明当前类对象,没有初始化
//4.此对象也必须声明为static
private static Order instance = null;
//3.声明public、static的返回当前类对象的方法
public static Order getInstance(){
if (instance == null){
instance = new Order();
}
return instance;
}
}
如何区分饿汉式、懒汉式
饿汉式:对象加载时间过长,线程安全的。
懒汉式:延迟对象的创建,线程不安全。
main方法的使用
1、main()方法作为程序的入口
2、main()方法也是一个普通的静态方法
3、main()也可以作为控制台交互的一种方式(之前是Scanner)
运行时传参
Run Configurations
代码块
代码块的作用:
1.初始化类、对象
2.如果有修饰只能使用static
3.分类:静态代码块、非静态代码块
静态代码块
内部可以有输出语句
随着类的加载而执行,只执行一次
作用:初始化类的信息
只能调用静态属性方法
非静态代码块
内部可以有输出语句
随着对象的创建而执行
每创建一个对象,就执行一次非静态代码块
作用:创建对象时,对对象的属性进行初始化
一个类中定义了多个非静态代码块,则按照声明的先后顺序执行
静态非静态的属性和方法都可以调用
关键字final
- final可以用来修饰的结构:类、方法、变量
修饰类:此类不能被其他类所继承,如:String类、System类、StringBuffer类
修饰方法:表明此方法不可以被重写,如:Object类、getClass类
修饰变量:此时的变量就称为一个常量
修饰属性:可以考虑赋值的位置有:显式初始化、代码块中初始化、构造器中初始化
修饰局部变量:修饰形参时,表明此形参是一个常量,当我们调用此方法时,给常量形参赋一个形实参,赋值以后就只能在方法体内使用此形参,不能重新赋值
static、final用来修饰属性:全局常量(一般属性大写来表示属性为常量)
abstract(抽象的)关键字
可以修饰的结构:类、方法
abstract修饰类:抽象类
此类不可实例化
抽象类中一定有构造器,便于子类实例化时调用(涉及:子类对象实例化的全过程)
开发中,都会提供抽象类的子类,让子类对象实例化,完成相关操作
abstract修饰方法:抽象方法
抽象方法只有方法的声明,没有 方法体
包含抽象方法的类,一定是一个抽象类。反之,抽象类中可以没有抽象方法的。
若子类重写了父类中的所有的抽象方法后,此子类方可实例化
若子类没有重写父类中的所有的抽象方法,则此子类也是一个抽象类,需要使用abstract修饰
abstract使用上的注意点:
abstract不能用来修饰:属性、构造器等结构
abstract不能用来修饰私有方法、静态方法、final的方法、final的类
package sxu.wmy05;
//抽象类的匿名对象
public class PersonTest {
public static void main(String[] args) {
//匿名对象非匿名类
method(new Student());
Worker worker = new Worker();
method1(worker);//非匿名类的非匿名的对象
method1(new Worker());//非匿名类的匿名对象
System.out.println("*****************");
//创建一匿名子类的 对象 : p
Person p = new Person() {
@Override
public void eat() {
}
@Override
public void breath() {
}
};//分号
method1(p);
}
public static void method1(Person p) {
p.eat();
p.wlak();
}
public static void method(Student s) {
}
static class Worker extends Person {
@Override
public void eat() {
}
@Override
public void breath() {
}
}
}
模板方法设计模式(TemplateMethod)
抽象类体现的就是一种模板模式的设计,抽象类作为多个子类的通用模板,子类在抽象类的基础上进行扩展、改造,但子类总体上会保留抽象类的行为方式。
模板方法设计模式是编程中经常用得到的模式。各个框架、类库中都有他的 影子,比如常见的有:
数据库访问的封装
Junit单元测试
JavaWeb的Servlet中关于doGet/doPost方法调用
Hibernate中模板程序
Spring中JDBCTemlate、HibernateTemplate等
package sxu.wmy07;
public class TemplateTest {
public static void main(String[] args) {
Template t = new SubTemplate();
t.spendTime();
}
}
abstract class Template{
public void spendTime(){
long start = System.currentTimeMillis();
code();//不确定的部分
long end = System.currentTimeMillis();
System.out.println("花费时间:" + (end - start));
}
public abstract void code();
}
class SubTemplate extends Template{
@Override
public void code() {
//1000以内的质数
for (int i = 2; i < 1000; i ++ ){
boolean isFlag = true;
for (int j = 2; j < Math.sqrt(i); i++){
if (i % j == 0){
isFlag = false;
break;
}
}
if (isFlag){
System.out.println(i);
}
}
}
}
接口(interface)
面向接口编程
接口(interface)是抽象方法和常量值定义的集合,Java中,接口和类是并列的两个结构
如何定义接口:定义接口中的成员
用interface来定义。
接口中的所有成员变量都默认是由public static final修饰的。
接口中的所有抽象方法都默认是由public abstract修饰的。
接口中不能定义构造器的!意味着接口不可以实例化
接口采用多继承机制。
Java开发中,接口通过让类去实现(implements)的方式来使用.
如果实现类覆盖了接口中的所有抽象方法,则此实现类就可以实例化
如果实现类没有覆盖接口中所有的抽象方法,则此实现类仍为一个抽象类
JDK7及以前:只能定义全局常量和抽象方法
>全局常量:public static final的.但是书写时,可以省略不写
>抽象方法:public abstract的
JDK8:除了定义全局常量和抽象方法之外,还可以定义静态方法、默认方法(略)
- 接口的具体使用,体现多态性
- 接口,实际上可以看做是一种规范
- 接口与接口之间可以继承,而且可以多继承
package com.atguigu.java1;
/*
* 接口的使用
* 1.接口使用上也满足多态性
* 2.接口,实际上就是定义了一种规范
* 3.开发中,体会面向接口编程!
*
*/
public class USBTest {
public static void main(String[] args) {
Computer com = new Computer();
//1.创建了接口的非匿名实现类的非匿名对象
Flash flash = new Flash();
com.transferData(flash);
//2. 创建了接口的非匿名实现类的匿名对象
com.transferData(new Printer());
//3. 创建了接口的匿名实现类的非匿名对象
USB phone = new USB(){
@Override
public void start() {
System.out.println("手机开始工作");
}
@Override
public void stop() {
System.out.println("手机结束工作");
}
};
com.transferData(phone);
//4. 创建了接口的匿名实现类的匿名对象
com.transferData(new USB(){
@Override
public void start() {
System.out.println("mp3开始工作");
}
@Override
public void stop() {
System.out.println("mp3结束工作");
}
});
}
}
class Computer{
public void transferData(USB usb){//USB usb = new Flash();
usb.start();
System.out.println("具体传输数据的细节");
usb.stop();
}
}
interface USB{
//常量:定义了长、宽、最大最小的传输速度等
void start();
void stop();
}
class Flash implements USB{
@Override
public void start() {
System.out.println("U盘开启工作");
}
@Override
public void stop() {
System.out.println("U盘结束工作");
}
}
class Printer implements USB{
@Override
public void start() {
System.out.println("打印机开启工作");
}
@Override
public void stop() {
System.out.println("打印机结束工作");
}
}
jdk8
public class SubClassTest {
public static void main(String[] args) {
SubClass s = new SubClass();
// s.method1();
// SubClass.method1();
//知识点1:接口中定义的静态方法,只能通过接口来调用。
CompareA.method1();
//知识点2:通过实现类的对象,可以调用接口中的默认方法。
//如果实现类重写了接口中的默认方法,调用时,仍然调用的是重写以后的方法
s.method2();
//知识点3:如果子类(或实现类)继承的父类和实现的接口中声明了同名同参数的默认方法,
//那么子类在没有重写此方法的情况下,默认调用的是父类中的同名同参数的方法。-->类优先原则
//知识点4:如果实现类实现了多个接口,而这多个接口中定义了同名同参数的默认方法,
//那么在实现类没有重写此方法的情况下,报错。-->接口冲突。
//这就需要我们必须在实现类中重写此方法
s.method3();
}
}
class SubClass extends SuperClass implements CompareA,CompareB{
public void method2(){
System.out.println("SubClass:上海");
}
public void method3(){
System.out.println("SubClass:深圳");
}
//知识点5:如何在子类(或实现类)的方法中调用父类、接口中被重写的方法
public void myMethod(){
method3();//调用自己定义的重写的方法
super.method3();//调用的是父类中声明的
//调用接口中的默认方法
//*******************
CompareA.super.method3();
CompareB.super.method3();
}
}
内部类
/*
* 类的内部成员之五:内部类
* 1. Java中允许将一个类A声明在另一个类B中,则类A就是内部类,类B称为外部类
*
* 2.内部类的分类:成员内部类(静态、非静态) vs 局部内部类(方法内、代码块内、构造器内)
*
* 3.成员内部类:
* 一方面,作为外部类的成员:
* >调用外部类的结构
* >可以被static修饰
* >可以被4种不同的权限修饰
*
* 另一方面,作为一个类:
* > 类内可以定义属性、方法、构造器等
* > 可以被final修饰,表示此类不能被继承。言外之意,不使用final,就可以被继承
* > 可以被abstract修饰
*
*
* 4.关注如下的3个问题
* 4.1 如何实例化成员内部类的对象
* 4.2 如何在成员内部类中区分调用外部类的结构
* 4.3 开发中局部内部类的使用 见《InnerClassTest1.java》
*
*/
在局部内部类的方法中(比如:show)如果调用局部内部类所声明的方法(比如:method)中的局部变量(比如:num)的话,要求此局部变量声明为final的。
jdk 7及之前版本:要求此局部变量显式的声明为final的
jdk 8及之后的版本:可以省略final的声明
【推荐】博客园的心动:当一群程序员决定开源共建一个真诚相亲平台
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】Flutter适配HarmonyOS 5知识地图,实战解析+高频避坑指南
【推荐】开源 Linux 服务器运维管理面板 1Panel V2 版本正式发布
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从“看懂世界”到“改造世界”:AI发展的四个阶段你了解了吗?
· 协程本质是函数加状态机——零基础深入浅出 C++20 协程
· 编码之道,道心破碎。
· 记一次 .NET 某发证机系统 崩溃分析
· 微服务架构学习与思考:SOA架构与微服务架构对比分析
· dotnetty 新的篇章- 开源
· 从“看懂世界”到“改造世界”:AI发展的四个阶段你了解了吗?
· 设计模式:简单工厂、工厂方法与抽象工厂
· 用好 JUnit 5 的高级特性:提升单测效率和质量
· 【大数据高并发核心场景实战】 - 数据持久化之冷热分离