JAVA基础学习
第二篇文章的连接: (超详细笔记整理)动力节点_老杜 | JavaSE进阶 【P486之后】.
方法
Java的主要内存空间

代码放在方法区侯先去执行main文件时,main方法在执行的时候 ,方法需要的内存空间在栈中被分配,栈是一种数据结构
栈数据结构
栈数据结构:stack
什么是数据结构?
数据结构通常是:存储数据的容器,而该容器可能存在不同的结构。数据结构和java语言实际上没有任何关系,数据结构是一门独立的学科
常见的数据结构是哪些?
数组、链表、图二叉、队列…java语言已经把数据结构写好了,程序员直接来用就可以。
和数据结构通常在一起的是算法:排序、查找等…
精通数据结构和算法,会提升程序的效率

方法区执行时内存的变换
入栈
弹栈
3个i和x都是局部变量,方法结束后,内存空间都进行释放
package Method;
//局部变量只在方法体中有效,方法结束后,局部变量内存就失效了
//方法区中最先有数据,方法区中放代码片段,存放class字节码
//栈内存:方法调用的时候,该方法需要的内存空间在栈中分配
//方法不调用是不会在栈中分配空间的,方法只有调用的时候才会在栈中分配空间,并且调用时就是压栈
//方法执行结束后,该方法所需要的空间就会被释放,进行弹栈。
/*
方法调用叫做:压栈,分配空间
方法结束:弹栈,释放空间
栈中存储:方法执行中需要的内存,以及该方法的局部变量
*/
public class test01 {
//主方法入口
public static void main(String[] args){
System.out.println("main begin");
/*
int a = 100;
int b = a;
这里的原理是:将a变量中保存的100这个数字复制一份传给b,
所以a和b是两个不同的内存空间,是两个局部变量
*/
int x = 100;
m1(x);
System.out.println("main begin");
}
public static void m1(int i){ //i是局部变量
System.out.println("m1 begin");
m2(i);
System.out.println("m1 over");
}
public static void m2(int i){
System.out.println("m2 begin");
m3(i);
System.out.println("m2 over");
}
public static void m3(int i){
System.out.println("m3 begin");
System.out.println(i);
System.out.println("m3 over");
}
}
main begin
m1 begin
m2 begin
m3 begin
100
m3 over
m2 over
m1 over
main begin

方法总结
方法的重载
package Method;
//程序先不使用方法重载,分析程序的缺点?
//程序员需要记忆过多的方法名字
//代码不够美观
public class OverloadTest01 {
//主方法
public static void main(String[] args){
int x = sumInt(10,20);
System.out.println(x);
long y = sumLong(20L,10L);
System.out.println(y);;
double z = sumDouble(10.00,21.00);
System.out.println(z);
}
public static int sumInt(int a, int b){
return a+b;
}
public static long sumLong(long a, long b){
return a+b;
}
public static double sumDouble(double a, double b){
return a+b;
}
}
package Method;
/*
使用方法重载机制
优点1:代码整齐美观
优点2:可以让功能相似的方法名重合
在java语言中如何进行语言区分?
首先Java编译器首先通过方法名进行区分,但是java语言中允许方法名相同的情况出现
如果方法名相同的情况下,编译器会通过方法的参数类型进行方法的区分
*/
public class overloadTest02 {
public static void main(String[] args){
System.out.println(sum(10, 20));
System.out.println(sum(10l, 20l));
System.out.println(sum(20.0, 10.0));
}
public static int sum(int a,int b){
System.out.println("int求和");
return a+b;
}
public static long sum(long a,long b){
System.out.println("long求和");
return a+b;
}
public static double sum(double a,double b){
System.out.println("double求和");
return a+b;
}
}
什么时候会发生方法重载?
在一个类当中功能一和功能二它们的功能是相似的;可以考虑他们的方法名是一致的,这样代码既美观,又便于后期编写
什么时候代码会发生方法重载?
- 条件一:在同一个类当中
- 条件二:方法名相同
- 条件三:参数列表不同
- 参数的类型不同;参数的个数不同;参数的顺序不同
注意:
- 方法重载和方法的返回值类型无关
- 方法重载和方法的修饰符列表无关
package Method;
/*
什么时候会发生方法重载?
在一个类当中功能一和功能二它们的功能是相似的
可以考虑他们的方法名是一致的,这样代码既美观,又便于后期编写
什么时候代码会发生方法重载?
条件一:在同一个类当中
条件二:方法名相同
条件三:参数列表不同
参数的类型不同;参数的个数不同;参数的顺序不同
*/
/*
大家是否承认println()是一个方法名字
*/
public class overloadTest03 {
public static void main(String[] args){
//println是方法名字,是由sun公司写的,可以直接使用
//println()方法肯定是重载了
//对于println只需要记住一个方法名就可以了
//参数类型可以随便传,这说明println()重载了
System.out.println(10);
System.out.println("12");
System.out.println(true);
}
}
方法递归
package Method;
/*
1.什么是方法递归?
方法自己调用自己,就是方法的递归
2.当递归时,程序没有结束条件,一定会发生,栈溢出错误
所以递归一定要结束条件
3.
*/
public class RecursionTest01 {
public static void main(String[] args){
doSome();
}
public static void doSome(){
System.out.println("doSome begin");
//调用方法:doSome()既是一个帆帆发,那么doSome方法可以调用吗?当然可以
//目前这个方法是没有停止条件的,电脑会出现什么问题?
doSome();
//这行永远执行不到
System.out.println("doSome over");
}
}
代码会出现栈的内存溢出错误
1.什么是方法递归?
方法自己调用自己,就是方法的递归
2.当递归时,程序没有结束条件,一定会发生,栈溢出错误所以递归一定要结束条件
3.递归假设有结束条件,就不会发生栈溢出错误么?
假设结束条件是对的,是合法的,递归有时候也会出现栈内存错误,因为有可能递归太深,占内存不够了,因为一直在压栈。
4.在实际的开发中不建议轻易的使用递归,能使用for循环while循环代替的,尽量使用循环来做,因为循环的效率高,耗费的内存少递归耗费的内存比较大,另外递归地使用不当可能会导致JWM死掉(但在极少数的情况下,不用递归程序没有办法实现)
5.在实际的开发中,如果遇到这个栈溢出的错误,如何解决这个问题?
- 第一步:先检查递归的结束条件是否正确
- 第二步:递归条件正确,这个时候需要手动调整JVM的栈内存初始化大小可以将栈内存的空间大一些。
- 第三步:调整了大小,如果运行时还是出现了错误,只能继续扩大栈内存的大小。
- java-x参数可以查看调整堆栈大小的参数
package Method;
/*
不用递归 计算1-n的和
*/
public class RecursionTest02 {
public static void main(String[] args){
int N = 100;
int res = sum(N);
System.out.println(res);
}
public static int sum(int n){
int result = 0;
for(int i = 1; i<= n ; i++){
result += i;
}
return result;
}
}
package Method;
/*
使用递归 求1-n的和
*/
public class RecursionTest03 {
public static void main(String[] args){
System.out.println(sum(100));
}
public static int sum(int n) {
if (n == 1) {
return 1;
}
return n + sum(n - 1);
}
}
递归内存图
递归求阶乘
package Method;
/*
使用递归 求阶乘
*/
public class RecursionTest03 {
public static void main(String[] args){
System.out.println(jicheng(5));
}
public static int jicheng(int n){
if(n ==1 ){
return 1;
}
return n * jicheng(n-1);
}
}
认识面向对象
1. 面向过程和面向对象有什么区别?
2. 当我们采用面向对象的方式贯穿整个系统的时候,涉及到三个术语:
OOA:面向对象的分析
OOD:面向对象的设计
OOP:面向对象的编程
整个软件开发的过程,都是采用OO进行贯穿的
实现一个软件的过程:
分析A ➡ 设计D ➡ 编程P
3. 面向对象的三大特征
封装、继承、多态
任何一个面向对象的编程语言都包括这三个特征:比如python
注意:java只是面向对象语言中的一种。
4. 类和对象的概念
什么是类?
类实际上在现实世界当中是不存在,是一个抽象的概念,是一个模板,是我们人类大脑进行思考的结果、总结、抽象的一个结果
明星是一个类
什么是对象?(真实存在个体)
对象是实际存在个体,对象还有另外一个名称叫做实例
通过类创建对象的过程,叫做实例化
姚明是一个对象
刘德华是一个对象
这两个对象都是明星这个类
在java语言中,要想得到“对象”,必须先定义“类”,“对象”就是通过“类”,这个模板创造出来的。
类就是一个模板:类中描述的是所有对象的“共同特征信息”
对象就是通过类创建出来的个体
类 ——[实例化] ——> 对象(实例)
对象 ——[抽象] ——> 类
类的共同特征
类是一个模板,是描述一个共同特征的模板,那么共同特征是什么?
状态特征、动作特征
类= 属性+ 方法
- 属性来源于:状态
- 方法来源于:动作
5. java软件工程师在开发中起到的作用是什么?
java是转换虚拟世界和现实世界的桥梁
6. 类的定义
怎么定义一个类,语法格式是什么?
[修饰符列表] class 类名{
//类= 属性 + 方法
//属性在代码上以“变量”的形式存在
//方法描述动作;属性描述状态
}
为什么属性以“变量”的形式存在?
属性对应的是数据,数据只能放到变量中
结论:属性就是变量
变量:根据出现的位置进行划分:
- 方法体中声明的变量:局部变量
- 方法体外声明的变量:成员变量
package lei;
/*
1.学生的共同特征:
学号int、姓名String、性别char、住址String
注意:属性是成员变量
*/
//Students既是一个类名又是一个类型名,属于引用数据类型
public class Students {
//属性
//成员变量
int xuehao;
String name;
int age;
boolean gender;
String adress;
}
package lei;
/*
对象的创建和使用
创建对象的语法是什么?
*/
public class StudentTest {
public static void main(String[] args){
//可以可以使用Student类的
/*
创建对象的语法;new
new 类名();new是一个运算符,专门负责对象的创建
数据类型 变量名 = new 类名();
数据类型包括两种:基本数据类型和引用数据类型
java中所有的”类“都是引用数据类型
*/
Students s = new Students();
//Students是一个引用数据类型
//new Students() 是一个对象
}
}
7. 编译的过程
对象的创建与使用
对象的创建
类名 变量名 = new 类名();
这样就完成了对象的创建
变量必须先声明,在赋值才能访问
注意:对于成员变量来说,没有手动赋值 ,系统自动赋值,为默认值
类型 默认值
byte 0
short 0
long 0L
int 0
float 0.0F
double 0.0
boolean flase
char \u000
引用数据类 null
什么是实例变量
实例是对象,对象又被称为实例;
实例变量实际上就是:对象级别的变量
堆内存中存储对象,以及对象的实例变量
实例变量在访问的时候,是不是必须先创建对象?
//访问学生姓名可以直接通过类名嘛?
//学生姓名是一个实例变量,实例变量是对象级别的变量
//是不是先有对象才可以有姓名
//不能通过类名直接访问实例变量
//System.out.println(Students.name);
对象和引用的区别?
对象是通过new出来,在堆内存中存储
引用是:但凡是变量,并且该变量中保存了内存地址指向了堆内存当中的对象的叫做引用
引用是:存储对象内存地址的一个变量
通俗一点说:只要这个变量中保存的是一个对象的内存地址,这个变量就叫做引用
对象是:堆里new出来的
访问id不能使用User.id,这是错误的,实例变量不能用类名访问。id的访问必须是先造对象,然后有了对象,才能访问对象的id
思考:引用一定是局部变量吗? 不一定是。
JVM中的三个主要内存
栈:主要存储局部变量以及类对象的地址,方法执行时所需要的空间,局部变量;只要是方法体中的局部变量,都在栈中进行分配
堆内存:主要存储对象以及对象的实例变量,实例变量
方法区:主要存储代码片段
内存图例子:
例子2:
package lei;
/*
记住:里面有什么就能点.什么
所有实例变量都是通过”引用.“来访问的
*/
public class Test01 {
public static void main(String[] args){
Address a = new Address();
a.city = "北京";
a.street = "大兴区";
a.zipcode = "10010";
System.out.println(a.city);
System.out.println(a.street);
System.out.println(a.zipcode);
Users user1 = new Users();
user1.add = a;
user1.id = 123;
user1.name = "yxy";
/*System.out.println("============”=“代表赋值运算===============");
int x = 100;
int y = x;
表示把x变量中保存的值100复制一份给y
*/
/*
Add K = new Add();
Add m = k;
Add k = 0x1111;
那么 m = 0x1111;
*/
//在java中只有值进行传递,值可能是变量、地址等等
System.out.println(user1.id);
System.out.println(user1.name);
System.out.println(user1.add.city);
System.out.println(user1.add.zipcode);
System.out.println(user1.add.street);
}
}
/*
*引用是:存储对象内存地址的一个变量**
通俗一点说:只要这个变量中保存的是一个对象的内存地址,这个变量就叫做引用
*对象是:堆里new出来的**
*
* 思考:引用一定是局部变量吗?
* 不一定是
*/
package lei;
public class Address {
String city;
String street;
String zipcode;
}
package lei;
public class Users {
int id;
String name;
Address add;
}
属性的引用
package lei;
public class T {
A a;
public static void main(String[] args){
A a = new A();
B b = new B();
C c = new C();
D d = new D();
T t = new T();
c.d = d;
b.c = c;
a.b = b;
t.a = a;
System.out.println(t.a.b.c.d.i);
}
}
class A{
B b;
}
class B{
C c;
}
class C{
D d;
}
class D{
int i = 100;
}
空指针
package lei;
/*
空指针异常
*/
public class NullPointerTest {
public static void main(String[] args){
//创建客户对象
Customer c = new Customer();
//访问这个对象的id
System.out.println(c.id);
//重新给id赋值
c.id = 45;
System.out.println(c.id);
c =null;
/*
编译器没问题,因为编译器只检查语法,编译器发现c是Customer类型,Customer类型中有id属性,所以可以
但是运行的时候需要对象的存在,但是对象没了,只能抛出一个异常
*/
System.out.println(c.id);
/*
空指针错误
Exception in thread "main" java.lang.NullPointerException
at lei.NullPointerTest.main(NullPointerTest.java:16)
*/
}
}
class Customer{
int id; //成员变量中的实例变量,应该先创建对象,然后通过”引用.“的方式访问
}
关于垃圾回收器GC,在java语言中,java回收器主要针对的是堆内存,当一个java对象没有任何引用指向该对象的时候,GC会考虑将垃圾数据释放被回收。
出现空指针异常的条件是:”空引用“访问实例相关数据时,都会出现空指针异常
方法调用时的参数传递
java中规定:参数传递的时候,和类型无关,无论是基本类型还是引用数据类型,同意都是盒子中保存的值,复制一份传递下去
package canshuchuandi;
//分析程序的参数传递
//java中规定:参数传递的时候,和类型无关,无论是基本类型还是引用数据类型,同意都是盒子中保存的值,复制一份传递下去
public class Test01 {
public static void main(String[] args){
int i = 10;
add(i);
System.out.println("main-->" + i); //10
}
public static void add(int i){ // i在add域中
//把main中i变量盒子中保存的10这个数复制了一份,传给了add方法
i++;
System.out.println("add-->" + i); //11
}
}
package canshuchuandi;
public class Test02 {
public static void main(String[] args) {
Person p = new Person();
p.age = 10;
add(p);
System.out.println("main -->" + p.age); //11
}
//方法可以是基本数据类型,也可以是引用数据类型,只要是合法的数据类型就可
public static void add(Person p){
p.age++;
System.out.println("add -->" + p.age); //11
}
}
class Person{
int age;
}
这两个p是不同的局部变量
构造方法Constructor
1.什么是构造方法?有什么用?
构造方法是一个比较特殊的方法,通过构造方法可以完成对象的创建以及实例变量的初始化,
换句话说构造方法是用来创建对象并且给对象的属性赋值的(注意:实例变量没有手动赋值的时候,系统会赋默认值)
2.当一个类没有提供任何构造方法,但是系统实际上会默认提供一个无参数的构造方法,(这个方法会被称为缺省构造器)
3.调用构造方法如何使用呢?
使用new运算符来构造方法;语法格式:new 构造方法名(实际参数列表)
4.构造方法的语法结构是什么?
[修饰符列表] 构造方法名(形式参数列表){
构造方法体;
}
注意:1.修饰符列表目前统一写public 千万不要写 public static
2.构造方法名和类名必须一致
3.构造方法名不需要指定任何返回值类型,不需要写void
普通方法的语法结构
[修饰符列表] 返回值类型 方法名(形式参数列表){
方法体;
}
5.无参数的构造方法和有参数的构造方法
-
当一个类中没有提供任何构造方法,系统默认提供一个无参数的构造方法,这个无参数的构造方法叫做缺省构造器。
-
如果一个类中手动的提供了构造方法,那么系统将不会提供无参构造方法了,建议手动将无参构造方法写出来,这样一定不会出问题
-
无参数的构造方法和有参数的构造方法都可以调用
-
构造方法支持方法重载么?
可以支持方法重载,在一个类中构造方法可以有多个,并且所有的构造方法的名字都是一样的
方法重载有一个特点:
在同一个类中,方法名字相同,参数列表不同
-
对于实例变量来说,只要你在构造方法中没有手动赋值,那么系统就会默认赋值
-
构造方法的作用:创建对象,并给属性赋值
package Constructor;
/*
构造方法:
1.什么是构造方法?有什么用?
构造方法是一个比较特殊的方法,通过构造方法可以完成对象的创建以及实例变量的初始化,
换句话说构造方法是用来创建对象并且给对象的属性赋值的(注意:实例变量没有手动赋值的时候,系统会赋默认值)
2.当一个类没有提供任何构造方法,但是系统实际上会默认提供一个无参数的构造方法,(这个方法会被称为缺省构造器)
3.调用构造方法如何使用呢?使用new运算符来构造方法
语法格式:new 构造方法名(实际参数列表)
4.构造方法的语法结构是什么? 没有返回值类型 不能写void
[修饰符列表] 构造方法名(形式参数列表){
构造方法体;
}
注意:1.修饰符列表目前统一写public 千万不要写 public static
2.构造方法名和类名必须一致
3.构造方法名不需要指定任何返回值类型,不需要写void
普通方法的语法结构
[修饰符列表] 返回值类型 方法名(形式参数列表){
方法体;
}
*/
public class Test01 {
public static void main(String[] args){
//调用无参数构造方法
new Students();
//调用普通方法
Test01.doSome();
doSome();
//调用Students类的无参数构造方法
//创建students类型的对象
Students s1 = new Students();
System.out.println(s1); //输出哈希值
//调用有参数的构造方法
Students s2 = new Students(100);
System.out.println(s2);
}
//普通构造方法
public static void doSome(){
System.out.println("ds");
}
}
class Students{
int no;
int age;
String name;
//当Students没有构造方法时,系统实际上会默认提供一个无参数的构造方法
//构造方法
public Students(){
System.out.println("无参数执行");
}
public Students(int no) {
this.no = no;
}
//重载
//public void Students(String name){}
public Students(String name){
this.name = name;
}
}
调用不同构造方法创造对象
package Constructor;
public class Test03 {
public static void main(String[] agrs){
//调用不同的构造方法创建对象
Vip v1 = new Vip();
Vip v2 = new Vip(111,"y");
Vip v3 = new Vip(222,"x","123");
Vip v4 = new Vip(333,"y","123",true);
System.out.println(v1.no);
System.out.println(v1.name);
System.out.println(v1.birth);
System.out.println(v1.sex);
System.out.println("----------------------");
System.out.println(v2.no);
System.out.println(v2.name);
System.out.println(v2.birth);
System.out.println(v2.sex);
System.out.println("----------------------");
System.out.println(v3.no);
System.out.println(v3.name);
System.out.println(v3.birth);
System.out.println(v3.sex);
System.out.println("----------------------");
System.out.println(v4.no);
System.out.println(v4.name);
System.out.println(v4.birth);
System.out.println(v4.sex);
}
}
0
null
null
false
----------------------
111
y
null
false
----------------------
222
x
123
false
----------------------
333
y
123
true
Process finished with exit code 0
面向对象的三大特征-封装、继承、多态
有了封装才有继承,有了继承,才有多态
封装
什么是封装?有什么用?
现实生活中有很多现实的例子都是封装的,例如:手机、电视机等
封装起来,保护内部的部件,另外封装之后, 对于我们使用者来说,我们是看不见内部的复杂结构的。
封装的作用:
一个类体当中的数据,假设封装之后,对于代码的调用人员来说,不需要关心代码的复杂实现,是需要通过一个简单的入口就可以访问了,另外,类体中安全级别较高的数据封装起来 ,外部人员不能随意访问,来保证数据的安全性。
怎么进行封装
第一步:属性私有化,private、
第二步:对外提供简单的操作入口,对外提供公开的set方法和get方法,并且这两个方法都不带有static,为对象级别的方法
可以在set方法中设立关卡,来保护数据的安全性
注意:java开发规范中,set方法和get方法要满足以下格式 不可以带static
get方法的要求:
public 返回值类型 get+属性名首字母大写(无参){
return xxx;
}
set方法的要求:
public void+属性名首字母大写(有一个参数){
xxx = 参数;
}
无封装的情况:
package fengzhuang;
//在外部程序中访问Person
public class PersonTest {
public static void main(String[] args){
//创建Person对象
Person p1 = new Person();
//访问对象的属性,一般分为两种操作,一种是读数据,一种是改数据
p1.age = 50;
System.out.println(p1.age);
//在Person这个外部程序中,目前可以随意对age进行操作
}
}
class Person {
//实例变量、属性
int age;
}
有封装的情况:
package fengzhuang;
//在外部程序中访问Person
public class PersonTest {
public static void main(String[] args) {
//private 表示私有的,被这个关键字修饰之后,该数据只能在本类中访问,除了这个类,这个属性将不能被访问
/*
Person age,彻底在外部不能进行访问
p1.age = 20;
//读取
System.out.println(p1.age);
*/
Person p1 = new Person();
//通过类名.不可以调用get和set方法。
p1.setAge(-1);
int age = p1.getAge();
System.out.println(age);
}
}
package fengzhuang;
//这是没有封装的Person
/*
public class Person {
//实例变量、属性
int age;
}
*/
//开始封装代码,不在对外暴露复杂的数据
//封装起来,保护内部的数据,保证其安全性
public class Person{
//属性私有化
private int age;
//对外提供简单的操作入口
/*
写专门的方法来完成读写(get、set)
对外提供公开的set方法和get方法,并且这两个方法都不带有static,为对象级别的方法
*/
//注意:java开发规范中,set方法和get方法要满足以下格式
/*
get方法的要求:
public 返回值类型 get+属性名首字母大写(无参){
return xxx;
}
set方法的要求:
public void+属性名首字母大写(有一个参数){
xxx = 参数;
}
*/
public int getAge(){
return age;
}
public void setAge(int age) {
if(age <0 || age >150){
System.out.println("年龄不合法");
return; //程序终止
}
this.age = age;
}
}
static关键字
静态变量在类加载时进行初始化
- 所有static关键字修饰的都是类相关的,类级别的
- 所有static修饰的,都是采用“类名."的方式访问的
- static修饰的变量:静态变量
- static修饰的方法:静态方法
变量的分类:
变量根据声明的位置进行划分:在方法体内的变量叫做:局部变量;
在方法体外的变量叫做:成员变量。
成员变量又可以分为:
实例变量
静态变量
1.当county不是静态变量的时候:会导致内存的浪费,country放在堆中
package Staticpackage;
/*
什么时候声明为静态的,什么时候声明为实例的
*/
public class Test02 {
public static void main(String[] args){
Chinese c1 = new Chinese("123456","yxy");
Chinese c2 = new Chinese("123456789","yxa");
}
}
class Chinese{
//身份证号应该为实例变量,一个人一份
//姓名也应该是实例变量
String idCard;
String name;
//国籍是一个固定值,所以是静态变量
String country;
public Chinese() {
}
public Chinese(String idCard, String name) {
this.idCard = idCard;
this.name = name;
}
}
2.当country为静态变量时,静态变量放在方法区当中
package Staticpackage;
/*
什么时候声明为静态的,什么时候声明为实例的
如果这个对象的某个属性值都是一样的,不建议定义为实例变量,建议定义为类级别特征,定义为静态变量,在方法区中保留,节省开销
*/
public class Test02 {
public static void main(String[] args){
//静态变量的输出
System.out.println(Chinese.country);
Chinese c1 = new Chinese("123456","yxy");
Chinese c2 = new Chinese("123456789","yxa");
System.out.println(c1.idCard);
System.out.println(c1.name);
System.out.println(c2.idCard);
System.out.println(c2.name);
//System.out.println(Chinese.idCard);
//程序报错,因为idCard时实例变量,必须通过引用.访问
}
}
class Chinese{
//身份证号应该为实例变量,一个人一份
//姓名也应该是实例变量
String idCard;
String name;
//声明为静态变量也具有初始值,为null,这个初始值在类加载时进行初始化
//并且静态变量存在方法区
//国籍是一个固定值,所以是静态变量
static String country = "中国";
public Chinese() {
}
public Chinese(String idCard, String name) {
this.idCard = idCard;
this.name = name;
}
}
3. 关于空指针异常
package Staticpackage;
/*
实例的:应引用.来访问
静态的:建议使用”类名.“来访问
结论:
空指针异常只有在”空引用“访问”实例“相关的,都会出现空指针异常
*/
public class Test03 {
public static void main(String[] args){
System.out.println(Chinese.country);
//创建对象
Chinese c1 = new Chinese("11", "yxy");
System.out.println(c1.country);
System.out.println(c1.name);
System.out.println(c1.idCard);
//空引用
c1 = null;
//不会出现空指针异常,因为静态变量不需要对象的存在,这个代码实际上还是System.out.println(Chinese.country);
System.out.println(c1.country);
}
}
class Chinese{
String idCard;
String name;
public Chinese() {
}
static String country = "中国";
public Chinese(String x, String y){
idCard = x;
name = y;
}
}
4. 静态方法的使用
package Staticpackage;
public class Test04 {
public static void main(String[] args){
Test04.dosome();
Test04 st = new Test04();
st.doOther();
}
//静态方法不需要new对象,采用类名.
public static void dosome(){
System.out.println("静态方法do some");
}
//实例方法都需要new对象,使用”引用.“来访问
public void doOther(){
System.out.println("实例方法");
}
}
5. 类中的东西
6. 方法什么时候定义为静态的
参考标准:当这个方法体当中,访问了实例变量,那么一定是实例方法。大部分情况下,如果是工具类的方法一般都是静态的(静态方法不需要new对象,直接采用类型调用,极其的方便,工具类就是为了方便,所以工具类中的方法一般为static)实例方法的定义:不同对象参加考试的结果不同,我们可以认为考试是与对象相关的东西,那么我们把考试定义为实例方法
package Staticpackage;
/*
什么时候定义为实例方法,什么时候定义为静态方法
参考标准:当这个方法体当中,访问了实例变量,那么一定是实例方法
大部分情况下,如果是工具类的方法一般都是静态的(静态方法不需要new对象,直接采用类型调用
极其的方便,工具类就是为了方便,所以工具类中的方法一般为static)
实例方法的定义:不同对象参加考试的结果不同,我们可以认为考试是与对象相关的东西,那么我们把考试定义为实例方法
*/
//类 = 属性 + 方法
public class Test05 {
public static void main(String[] args){
}
}
class User{
//实例变量,需要对象r
private int id;
private String name; //首先name是对象级别的。
//打印用户的名字,是实例方法
public void printName(){
}
// 先new一个对象才可以get和set方法
public void setId(int i){
id = i;
}
public int getId(){
return id;
}
}
7. 不可以在main方法中定义static变量的原因
只有类才存在静态的变量,方法只能对静态变量的操作,不能在方法内试图定义静态变量
static静态块
- static可以定义静态代码块
语法结构:
static{
java语句;
java语句;
}
-
执行时间:类加载时执行,并且只执行一次
-
注意:一个类当中可以写多个静态代码块,静态代码块在类加载时执行,并且在main方法执行前执行,静态代码块一般按照自上而下的顺序执行
-
静态代码块的作用:记录项目日志
特殊的时机:类加载的时机,放代码
package Staticpackage;
/*
static可以定义静态代码块
语法结构:
static{
java语句;
java语句;
}
执行时间:
类加载时执行,并且只执行一次
特点:
一个类当中可以写多个静态代码块,静态代码块在类加载时执行,并且在main方法执行前执行,静态代码块一般按照自上而下的顺序执行**
*/
public class Test06 {
//静态代码块
static{
System.out.println("A");
}
//一个类中可以定义多个静态代码块
static{
System.out.println("B");
}
public static void main(String[] agrs){
System.out.println("hello word");
}
static{
System.out.println("C");
}
}
静态变量在类加载时初始化,静态代码块在类加载时执行,所以静态代码块可以访问静态变量,
实例变量的初始化是在new对象的时候开始,在构造方法执行时内存空开才会开辟
总结:栈、堆、方法区
栈:只要方法执行,就会进行压栈操作,栈会提供方法所需要的空间,栈内存放局部变量
堆:堆内存放实例变量,new出来的对象
方法区:类的信息、存放代码片段和静态变量
到目前为止,有顺序要求的执行代码有哪些?
实例语句块
package Staticpackage;
public class InstanceCode {
//入口
public static void main(String[] args){
new InstanceCode();
new InstanceCode();
new InstanceCode("abc");
}
//实例语句块 不在类加载时执行
{
System.out.println("实例语句执行");
}
public InstanceCode() {
System.out.println("无参数构造");
}
public InstanceCode(String name) {
System.out.println("有参数构造");
}
}
实例语句块,不在类加载时执行,在构造器执行之前执行,并且在每一次new对象时,都执行一次
所学的各个代码块的执行顺序
package Staticpackage;
//判断执行顺序
public class CodeOrder {
static{
System.out.println("A");
}
public static void main(String[] args){
System.out.println("main begin");
new CodeOrder();
System.out.println("main over");
}
public CodeOrder() {
System.out.println("B");
}
{
System.out.println("C");
}
static{
System.out.println("X");
}
}
A
X
main begin
C
B
main over
this关键字
-
this 是一个关键字,全部小写
-
一个对象一个this,this是一个变量,是一个引用。this保存当前对象的内存地址,指向自身,所以,严格意义上来说,this代表的是“当前对象”,this存储在堆内存当中对象的内部。
-
this只能使用在实例方法中。谁调用这个实例方法,this就是谁,所以this代表的是当前对象。
-
this.大部分情况下是可以省略的
-
为什么this不可以使用在静态方法中?
this代表的是当前对象,而静态方法的调用不需要对象
-
在实例方法中,或者构造方法中,为区分局部变量和实例变量,这种情况下this是不可以省略的
//this不可以省略的情况 package Staticandthis; /* this 可以使用在实例方法中,不能使用在静态方法中,this关键字大部分情况下可以省略 在实例方法中,或者构造方法中,为区分局部变量和实例变量,这种情况下this是不可以省略的 */ public class Test09 { public static void main(String[] args){ System.out.println("——————无参数构造——————"); Students s = new Students(); s.setName("yxy"); s.setNo(1); System.out.println(s.getName()); System.out.println(s.getNo()); System.out.println("——————有参数构造——————"); Students s2 = new Students(2,"yx"); System.out.println(s2.getName()); System.out.println(s2.getNo()); } } class Students{ private int no; private String name; public Students() { } //this.no是一个实例变量 // no是一个局部变量 public Students(int no, String name) { this.no = no; this.name = name; } public void setNo(int no) { this.no = no; } public void setName(String name) { this.name = name; } //getName实际上获取的是当前名字的 public int getNo() { return no;//return this.name } public String getName() { return name; } }

package Staticandthis;
/*
1.this是一个关键字,全部小写
2.this是什么,在内存方面是怎么样的?
this是一个变量,是一个引用,this保存当前对象的内存地址,指向自身
一个对象一个this
所以严格意义上来说,this代表的就是“当前对象”,this存储在堆内存当中
*/
public class Test08 {
public static void main(String[] args){
Customer c1 = new Customer("yxy");
c1.shopping(); //this 代表c1,c1调用shopping,this就是c1
Customer c2 = new Customer("x");
Student.m();
}
}
class Customer {
String name;
public Customer() {
}
public Customer(String s) {
name = s;
}
//顾客购物方法 实例方法
public void shopping() {
//c1调用shopping,this就是c1
//c2调用shopping,this就是c2
System.out.println(this.name + "正在购物");//引用.就是this. this.是可以省略的
}
public static void doSome() {
//this代表的是当前对象,而静态方法的调用不需要对象
// System.out.println(this);
}
}
class Student{
//实例变量,必须new对象,通过引用.来访问
String name = "z";
//所以name不能访问
public static void m(){
// System.out.println(name);
//可以这样
Student s = new Student();
System.out.println(s.name);
//方法中直接访问了实例变量,那么必须是实例方法
}
}
yxy正在购物
z
-
新语法:通过当前的构造方法去调用另一个本类的构造方法,可以使用以下语法格式:
this(实际参数列表);
通过一个构造方法1去调用构造方法2,可以做到代码复用,但是需要注意的是:构造方法1和构造方法2都是在同一个类当中
新语法的作用是:代码复用
在构造方法中:this()前面不可以有其他的语句,并且只可以出现一次。
package Staticandthis; /* this` 可以用在实例方法以外,还可以用在构造方法中 新语法:通过当前的构造方法去调用另一个本类的构造方法,可以使用以下语法格式:this(实际参数列表); */ public class Test10 { public static void main(String[] args) { Date d1 = new Date(); d1.setDay(10); Date d2 = new Date(2000, 01, 01); d1.detail(); d2.detail(); } } class Date{ private int year; private int month; private int day; public Date(){ //错误:对this 的调用必须是构造器中的第一个语句 // System.out.println(11); /* this.year = 1970; this.month = 1; this.day = 1;*/ this(1970,1,1); } public Date(int year, int month, int day) { this.year = year; this.month = month; this.day = day; } //提供一个打印方法 public void detail(){ System.out.println(year + "," + month + "," + day); } public void setYear(int year) { this.year = year; } public void setMonth(int month) { this.month = month; } public void setDay(int day) { this.day = day; } public int getYear() { return year; } public int getMonth() { return month; } public int getDay() { return day; } }
总结
package Staticandthis;
public class Test11 {
public static void main(String[] args){
//创建账户
Account a = new Account("1000",2000,1.23);
//创建客户对象
//传a是想让Customer和Account对象产生关系
//把a的内存地址直接赋值给变量act
Customers c = new Customers("yx",a);
c.getAct().deposit(100);
c.getAct().withdraw(960);
c.getAct().withdraw(2000);
}
}
//账户类
class Account{
private String id;
private double balacen;
private double annualInterestRate;
public Account() {
}
public Account(String id, double balacen, double annualInterestRate) {
this.id = id;
this.balacen = balacen;
this.annualInterestRate = annualInterestRate;
}
public void setId(String id) {
this.id = id;
}
public void setBalacen(double balacen) {
this.balacen = balacen;
}
public void setAnnualInterestRate(double annualInterestRate) {
this.annualInterestRate = annualInterestRate;
}
public String getId() {
return id;
}
public double getBalacen() {
return balacen;
}
public double getAnnualInterestRate() {
return annualInterestRate;
}
//存款方法
public void deposit(double money){
if(money > 0){
// balacen = money + balacen;
// return balacen;
// 调用方法来修余额
setBalacen(getBalacen() + money);
System.out.println("成功存入" + money);
}else {
System.out.println("请输入正确金额");
return;
}
}
//取款方法
public void withdraw(double money){
if(money < getBalacen()){
//balacen = balacen -money;
//return balacen;
setBalacen(getBalacen() - money);
System.out.println("成功取出" + money);
}else {
System.out.println("余额不足");
return;
}
}
}
class Customers{
private String name;
private Account act;
public Customers() {
}
public Customers(String name, Account act) {
this.name = name;
this.act = act;
}
public String getName() {
return name;
}
public Account getAct() {
return act;
}
public void setName(String name) {
this.name = name;
}
public void setAct(Account act) {
this.act = act;
}
}
java三大变量
总结:
所有的变量如何访问
所有的方法如何访问
一个类中都有什么
程序无论怎么变化,有一个固定的规律
所有的实例相关的都是先创建对象,通过”引用.“来访问
所有的静态相关的都是直接采用”类名.“来访问
结论:
只要是负责调用的方法a和被调用的方法b在同一个类当中:
this.
类名. 可以省略
package Staticandthis;
/*
类体{
实例变量;
实例方法;
静态变量;
静态方法;
构造方法;
静态代码块;
实例语句块;
方法(){
局部变量;
}
}
*/
public class Review {
//在程序执行之前,将所有的类全部加载到JVM中
//先完成加载才会执行main方法
static{
System.out.println("Review类加载");
}
//静态方法
public static void main(String[] args){
//局部变量
int i = 100;
//完成全部的动作
stu s1 = new stu();
s1.study();
}
}
//学生类
class stu{
private int no;
private String name;
static String job = "学习";
{
System.out.println("实例语句块,这个构造方法执行一次,这里就执行一次");
}
public stu(int no, String name) {
this.no = no;
this.name = name;
}
public stu() {
//默认学生的学号和姓名
this(100,"yxy");
}
//提供两个实例方法
//在实例方法中调用本类其他的实例方法
public void study(){
//System.out.println(this.getName() + "在努力学习");
System.out.println(name + "在努力学习");
//this.可以省略
//eat();
this.eat();
}
public void eat(){
System.out.println(name + "在餐厅吃饭");
//静态方法使用类名.的方式
//在同一个类当中,类名.可以省略
stu.m1();
}
//提供两个静态方法
public static void m1(){
System.out.println("M1 method");
m2();
}
public static void m2(){
System.out.println("M2 method");
System.out.println("状态" + job);//stu.job
}
public int getNo() {
return no;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
package Staticandthis;
/*
程序无论怎么变化,有一个固定的规律
所有的实例相关的都是先创建对象,通过”引用.“来访问
所有的静态相关的都是直接采用”类名.“来访问
结论:
只要是负责调用的方法a和被调用的方法b在同一个类当中:
this.
类名. 可以省略
*/
public class Review02 {
int i = 100;
static int j = 1000;
public void m1(){}
public void m2(){}
public void x(){ // 这个方法是实例方法,执行这个方法的过程中,当前对象是存在的
m1(); //this.m1();
m2();
m3();
m4();
System.out.println(i);
System.out.println(j);
//访问其他类的静态方法
T.t2();
//访问其他类的实例方法
T t = new T();
t.t1();
}
public static void m3(){}
public static void m4(){}
/*
第一步:
main 方法是静态的,JVM调用main方法的时候直接采用的是”类名.“的方式、所以main方法中没有this
第二步:
m1() m2()都是实例方法,按照java语法来说,应该先new对象,在用过”引用.“的方式进行访问
*/
public static void main(String[] args){
// System.out.println(i);
System.out.println(j);
// m1();
// m2();
m3();
m4();
//想要访问m1 m2 先new对象
Review02 r = new Review02();
r.m1();
r.m2();
System.out.println(r.i);
//局部变量 可以直接访问
int k = 10;
System.out.println(k);
T t = new T();
t.t1();
T.t2();
}
}
class T{
public void t1(){}
public static void t2(){}
}
继承
继承的作用:
基本作用:子类继承父类,代码可以得到复用
主要作用:因为有了继承关系,才有了后期的方法覆盖和多态
继承的相关特性:
① B类继承 A类,则称 A类为超类(superclass)、父类、基类,B类则称为子类(subclass)、 派生类、扩展类。
② java 中的继承只支持单继承,不支持多继承,C++中支持多继承,这也是 java 体 现简单性的一点,换句话说,java 中不允许这样写代码:
class B extends A,C{ }。
③ 虽然 java 中不支持多继承,但有的时候会产生间接继承的效果,
例如:class C extends B,class B extends A,也就是说,C 直接继承 B,其实 C 还间接继承 A。
④ java 中规定,子类继承父类,除构造方法外,剩下都可以继承。但是私有的属性无法在子类中直接访问,可以通过间接的手段
⑤ java 中的类没有显示的继承任何类,则默认继承 Object 类,Object 类是 java 语言提供的根类(老祖宗类),也就是说,一个对象与生俱来就有 Object 类型中所有的特征。
⑥ 继承也存在一些缺点,例如:CreditAccount 类继承 Account 类会导致它们之间耦合度非常高,Account 类发生改变之后会马上影响到 CreditAccount 类
对继承自Object
测试extends
1. 子类继承父类后,可以使用子类对象调用父类方法吗?
本质上,子类继承父类后,是将父类继承过来的方法归自己所有,实际上调用的也不是父类的方法,是子类自己的方法,是属于子类的方法
package Extends;
/*
测试,子类继承父类,可以使用子类对象调用父类对象吗?
本质上,子类继承父类后,是将父类继承过来的方法归自己所有,实际上调用的也不是父类的方法,是子类自己的方法,是属于子类的方法
*/
public class Test02 {
public static void main(String[] args){
Cats c = new Cats();
c.move();
System.out.println(c.name);
}
}
class Animal{
//名字不封装
String name = "x";//给一个默认值
public void move(){
System.out.println(name + "正在移动");
}
}
//Cats继承Animal会将所有的东西继承过来
class Cats extends Animal{
}
2. 在实际开发中,满足什么条件是可以使用继承?
凡是采用“is a”能描述的,都可以继承
例如:cat is a animal
假设以后的开发中,有一个A类和一个B类,A类和B类确实有重复的代码,那么他们两个之间就可以继承吗?不一定,还要看一看他们之间是否能够用“is a”来描述
class Customer{
String name; // 名字
// setter and getter
}
class Product{
String name; // 名字
// setter and getter
}
class Product extends Customer{
}
以上的继承就属于很失败的。因为:Product is a Customer,是有违伦理的。
3. 任何一个类,没有显示继承任何类,默认继承Object,那么Object中都有什么?
java为什么比较好学呢?
是因为Java内置了一套庞大的类库,程序员不需要从0开始写代码,程序员可以基于这套庞大的类库进行“二次”开发。(开发速度较快,因为JDK内置的这套库实现了很多基础的功能。)
例如:String是SUN编写的字符串类、System是SUN编写的系统类。这些类都可以拿来直接使用。
JDK源代码在什么位置?
C:\Program Files\Java\jdk-13.0.2\lib\src.zip
你现在能看懂以下代码了吗?
System.out.println(“Hello World!”);
System.out 中,out后面没有小括号,说明out是变量名。
另外System是一个类名,直接使用类名System.out,说明out是一个静态变量。
System.out 返回一个对象,然后采用“对象.”的方式访问println()方法
package Extends;
//idea中 蓝色是关键字
//黑色的标识符
//System.out.println("Hello word");
//以上代码中System、out、println都是标识符
public class Test03 {
static Student s = new Student();
public static void main(String[] args){
//入口
Test03.s.ex();//类.静态变量.方法
System.out.println("Hello word");
}
}
class Student{
public void ex(){
System.out.println("考试。");
}
}
Object其中有一个叫做toString()的,我们进行了测试,发现:
System.out.println(引用);
当直接输出一个“引用”的时候,println()方法会先自动调用“引用.toString()”,然后输出toString()方法的执行结果。
package Extends;
/*
默认继承Object,Object中有哪些方法?
package java.lang;
import jdk.internal.vm.annotation.IntrinsicCandidate;
public class Object {
@IntrinsicCandidate
public Object() {}
@IntrinsicCandidate
public final native Class<?> getClass();
@IntrinsicCandidate
public native int hashCode();
public boolean equals(java.lang.Object obj) {
return (this == obj);
}
@IntrinsicCandidate
protected native java.lang.Object clone() throws CloneNotSupportedException;
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
@IntrinsicCandidate
public final native void notify();
@IntrinsicCandidate
public final native void notifyAll();
public final void wait() throws InterruptedException {
wait(0L);
}
public final native void wait(long timeoutMillis) throws InterruptedException;
public final void wait(long timeoutMillis, int nanos) throws InterruptedException {
if (timeoutMillis < 0) {
throw new IllegalArgumentException("timeoutMillis value is negative");
}
if (nanos < 0 || nanos > 999999) {
throw new IllegalArgumentException(
"nanosecond timeout value out of range");
}
if (nanos > 0 && timeoutMillis < Long.MAX_VALUE) {
timeoutMillis++;
}
wait(timeoutMillis);
}
@Deprecated(since="9")
protected void finalize() throws Throwable { }
}
*/
public class Test05 {
public static void main(String[] args){
Test05 t = new Test05();
String r = t.toString();
System.out.println(r); //Extends.Test05@1b6d3586 1b6d3586 可以等同看作是对象在堆内存当中的内存地址,实际上是内存地址经过哈希算法得出的结果
product p = new product();
String r2 = p.toString();
System.out.println(r2);
System.out.println(p.toString());
//如果直接输出引用呢
System.out.println(p);//默认调用toString方法
}
}
class product{
/*
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
*/
}
Extends.Test05@1b6d3586
Extends.product@4554617c
Extends.product@4554617c
Extends.product@4554617c
方法的覆盖和多态机制
方法覆盖
什么时候使用方法覆盖?
当子类继承父类之后,当继承过来的方法无法满足当前子类的业务需求时,子类有权利对这个方法进行重新编写,有必要进行方法的覆盖,方法覆盖又叫做方法重写Override
方法覆盖的条件
条件一:两个类必须具有继承关系
继承的两个作用:基本作用和重要作用
基本作用:代码复用
重要作用:方法覆盖和多态机制
条件二:重写之后的方法和继承过来的方法具有
相同的返回值类型,
相同的方法名,
相同的形式参数列表
条件三:访问权限不能更低,可以更高
条件四:重写之后的方法不能比之前的方法抛出的更多,可以更少
package Override;
/*
什么时候会考虑使用方法覆盖:当子类继承父类之后,当继承过来的方法无法满足当前子类的业务需求时,子类有权利对这个方法进行重新编写,有必要进行方法的覆盖,
方法覆盖又叫做方法重写Override
重要结论:
当子类对父类继承过来的方法进行方法重写之后,那么创建子类对象,对象调用的一定是覆盖后的方法
回顾以下方法重载:
什么时候开率使用方法重载?overload
当在一个类当中,如果功能相似的话,建议将名字定义的一样,这样代码美观,并且方便编程
什么条件满足方法重载?
条件一:在同一个类当中
条件二:方法名相同
条件三:参数列表不同(个数,顺序,类型)
当我们代码怎么编写的时候,在代码级别上构成了方法覆盖呢?
条件一:两个类必须具有继承关系
继承的两个作用:基本作用和重要作用
基本作用:代码复用
重要作用:方法覆盖和多态机制
条件二:重写之后的方法和继承过来的方法具有
相同的返回值类型,
相同的方法名,
相同的形式参数列表
条件三:访问权限不能更低,可以更高
条件四:重写之后的方法不能比之前的方法抛出的更多,可以更少
*/
public class Test02 {
public static void main(String[] args){
Brid b =new Brid();
b.move();
b.sing(1);
Cat c = new Cat();
c.move();
}
}
class Animal{
public void move(){
System.out.println("动物在移动!");
}
//父类有抛出异常
/*public void move() throws Error{
System.out.println("动物在移动!");
}*/
//更低的权限可以被public访问
/*protected void move(){
System.out.println("动物在移动!");
}*/
public void sing(int i){
System.out.println("Animal singing");
}
}
class Brid extends Animal{
//对move方法进行方法覆盖,方法重写,override
//最好将父类中的方法原封不动的复制过来(不建议手动编写)
//方法覆盖就是将继承过来的方法覆盖掉了
public void move(){
System.out.println("鸟在飞行!");
}
//子类抛出的异常不能比父类多
/*public void move() {
System.out.println("动物在移动!");
}*/
//protected表示受保护的,没有public开放
//错误:访问权限太低,
/* protected void move(){
System.out.println("鸟在飞");
}*/
//没够构成方法覆盖
//但是可以构成方法重载
public void sing(){
System.out.println("Bird singing");
}
}
class Cat extends Animal{
public void move(){
System.out.println("猫在走");
}
}
注意事项
- 注意1:方法覆盖只是针对于方法与属性无关
- 注意2:私有方法无法覆盖
- 注意3:构造方法不能被继承,所以构造方法也不能被覆盖
- 注意4:方法覆盖只能针对于治理方法,静态方法没有覆盖意义
经典的案例
package Override;
//一个方法覆盖比较经典的案例
public class Test03 {
public static void main(String[] args){
//第一种方法
/*Chinese c = new Chinese("中国人");
c.speak();
American a = new American("美国人");
a.speak();*/
//第二种方法
Chinese c = new Chinese();
c.setName("中国人");
c.speak();
American a = new American();
a.setName("美国人");
a.speak();
}
}
class People{
private String name;
public People() {
}
public People(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void speak(){
System.out.println(name + "正在说话");
}
}
class Chinese extends People{
public Chinese(String name) {
super(name);
}
public Chinese() {
}
public void speak(){
//name为私有属性,只有调用grtName()才可以调用name
System.out.println(this.getName() + "说汉语");
}
}
class American extends People{
public American(String name) {
super(name);
}
public American() {
}
public void speak(){
//name为私有属性,只有调用grtName()才可以调用name
System.out.println(this.getName() + "说英语");
}
}
package Override;
/*
关于Object中的toString()方法
1.toString()方法的作用是什么?
将java对象转换成字符串形式
2.Object类中toSting()方法的默认实现是什么?
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
toSrting:意思是转换成String
含义:调用一个Java对象的toString方法就可以将该Java对象转化成字符串形式。
3.那么toString()方法给的默认实现够用么?
*/
public class Test04 {
public static void main(String[] args){
MyDate t1 = new MyDate();
//将对象转换为字符串形式
//MyDate@776ec8df 是重写toString方法之前的结果
//希望输出:xxxx年xx月xx日
//1970年1月1日 重写之后的输出
System.out.println(t1.toString());
//当输出一个引用的时候,println方法会自动调用引用的toString方法
System.out.println(t1);
MyDate t2 =new MyDate(2000,8,7);
System.out.println(t2.toString());
System.out.println(t2);
}
}
class MyDate{
private int year;
private int month;
private int day;
public MyDate() {
//默认时间
this(1970,1,1);
}
public MyDate(int year, int month, int day) {
this.year = year;
this.month = month;
this.day = day;
}
public int getYear() {
return year;
}
public int getMonth() {
return month;
}
public int getDay() {
return day;
}
public void setYear(int year) {
this.year = year;
}
public void setMonth(int month) {
this.month = month;
}
public void setDay(int day) {
this.day = day;
}
public String toString() {
return year + "年" + month + "月" + day + "日";
}
}
方法重载和方法覆盖有什么区别?
方法重载发生在同一个类当中。
方法覆盖是发生在具有继承关系的父子类之间。
方法重载是一个类中,方法名相同,参数列表不同。
方法覆盖是具有继承关系的父子类,并且重写之后的方法必须和之前的方法一致:
方法名一致、参数列表一致、返回值类型一致。
多态
向上转型和向下转型的概念
第一个:向上转型
子 --> 父 (自动类型转换)
第二个:向下转型
父 --> 子 (强制类型转换,需要加强制类型转换符)
注意:
Java允许向上转型,也允许向下转型
无论是向上转型,还是向下转型
两种类型之间必须有继承关系,没有继承关系编译器报错

什么是多态?
多种状态
Java程序分为编译阶段和运行阶段
先来分析编译阶段:
对于编译器来说,编译器只知道a2的类型是Animal
所以编译器在编译的时候,会去Animal.class字节码文件中去找move方法,找到了绑定上move方法,编译成功,静态绑定成功
分析运行阶段:
运行阶段的时候,实际上在堆内存中存在的java对象是Cat对象,所以move的时候,真正参与move的对象是Cat,所以运行阶段会动态执行Cat对象的move()方法,这个过程属于运行阶段的绑定,属于动态绑定
多态表示多种形态:
编译的时候一种形态
运行的时候是一种形态。
向下转型的风险
会产生ClassCastException的错误
怎么避免:ClassCastException的产生
新的内容,运算符:instanceof
第一:instanceof可以在运行阶段动态判断引用指向的对象的类型
第二:引用instanceof的语法:
(引用instanceof类型)
第三:instanceof运算符的运算结果只能是:true/fasle
第四:c是一个引用,c变量保存了内存地址,指向了堆中的对象
假设(c instanceof Cat)为True; 表示c内存中的java对象是一个Cat
假设(c instanceof Cat)为false;表示c内存中的java对象不是一个Cat
instanceof在运行阶段动态判断。
任何时候任何地点,在对类型进行向下转型时,一定要使用instanceof运算符,可以很好的避免ClassCastException
package duotai;
/*
多态的基础语法
1.学习多态基础语法之前,我们需要普及两个概念:
第一个:向上转型
子 --> 父
第二个:向下转型
父 --> 子
注意:
Java允许向上转型,也允许向下转型
无论是向上转型,还是向下转型
两种类型之间必须有继承关系,没有继承关系编译器报错
2.多态指的是:
父类型引用指向子类的对象
包括编译阶段和运行阶段
编译阶段:绑定父类型的方法
运行阶段:动态绑定子类型的方法
3.什么时候使用向下转型?
不要随便做强制类型转换
子类对象中特有的方法,使用向下转型
*/
public class Test01 {
public static void main(String[] args){
Animal a1 = new Animal();
a1.move();
Cat c1 = new Cat();
c1.move();
Bird b1 = new Bird();
b1.move();
/*
1.Animal和Ca之间有继承关系
2.Animal是父类,Animal是子类
3.Cat is a Animal
父类型的引用允许指向子类型的对象。
Animal a2 = new Cat();
a2就是父类型的引用
new Cat()就是一个子类型的对象
允许a2这个父类型引用指向子类型的对象。
*/
Animal a2 = new Cat();
Animal a3 = new Bird();
/*
什么是多态? 多种状态
Java程序分为编译阶段和运行阶段
先来分析编译阶段:
对于编译器来说,编译器只知道a2的类型是Animal
所以编译器在编译的时候,会去Animal.class字节码文件中去找move方法,找到了绑定上move
方法,编译成功,静态绑定成功
分析运行阶段:
运行阶段的时候,实际上在堆内存中存在的java对象是Cat对象,所以move的时候,真正参与move的对象是Cat
所以运行阶段会动态执行Cat对象的move()方法,这个过程属于运行阶段的绑定,属于动态绑定
多态表示多种形态:
编译的时候一种形态
运行的时候是一种形态。
*/
a2.move();
a3.move();
//====================================================================
Animal a5 = new Cat();
//分析程序一定要分析编译阶段和运行阶段的动态绑定
//若调用CatchMouse就必须进行强制向下转型
//因为a5是Animal类型,转成Cat,Animal和Cat之间存在继承关系,所以才没有报错
Cat x = (Cat)a5;
x.CatchMouse();
//向下转型有风险吗
Animal a6 = new Bird();//表面上a6是一个Animal,运行的时候实际上是Brid
//运行阶段,堆内存实际上创建的是Bird对象,在实际运行中,Bird 对象转换成Cat对象,
//bird和Cat对象之间没有继承关系
/* Cat y = (Cat)a6;
y.CatchMouse();*/
//怎么避免:ClassCastException的产生
/*
新的内容,运算符:instanceof
第一:instanceof可以在运行阶段动态判断引用指向的对象的类型
第二:引用instanceof的语法:
(引用instanceof类型)
第三:instanceof运算符的运算结果只能是:true/fasle
第四:c是一个引用,c变量保存了内存地址,指向了堆中的对象
假设(c instanceof Cat)为True; 表示c内存中的java对象是一个Cat
假设(c instanceof Cat)为false;表示c内存中的java对象不是一个Cat
instanceof在运行阶段动态判断。
任何时候任何地点,在对类型进行向下转型时,一定要使用instanceof运算符,可以很好的避免ClassCastException
*/
System.out.println(a6 instanceof Cat);
if(a6 instanceof Cat){//如果a6为Cat则进行强制类型转换
Cat y = (Cat)a6;
y.CatchMouse();
}
}
}
class Animal{
public void move(){
System.out.println("moving");
}
}
class Cat extends Animal{
@Override
public void move() {
System.out.println("Cat is Moving");
}
//除了move之外,应该有自己特有的行为
public void CatchMouse(){
System.out.println("catching");
}
}
class Bird extends Animal{
@Override
public void move() {
System.out.println("Bird is flying");
}
public void Sing(){
System.out.println("bird is singing");
}
}
package duotai;
/*
程序员可以观察到底层,到底是Bird和Cat
进行instanceof判断是因为:在以后的开发中,程序员可能看不到
*/
public class Test02 {
public static void main(String[] args){
Animal x = new Bird();
Animal y = new Cat();
if(x instanceof Bird){
Bird a = (Bird)x;
a.Sing();
}else if(x instanceof Cat){
Cat a = (Cat)x;
a.CatchMouse();
}
if(y instanceof Cat){
Cat b = (Cat)y;
b.CatchMouse();
}else if(y instanceof Bird){
Bird b = (Bird)y;
b.Sing();
}
}
}
package duotai;
public class Test03 {
public static void main(String[] args){
//main程序员A负责编写
Atest a = new Atest();
a.test(new Cat());
a.test(new Bird());
}
}
class Atest{
//程序员B负责编写
//这个test()方法的参数是一个Animal
public void test(Animal a){
//你写的这个方法别人会调用
//别人调用的时候可能给你test()方法传过来一个Bird
//当然可能识别的
//对于我来说,不知道调用的时候,会传给我什么参数
if(a instanceof Cat){
Cat c = (Cat) a;
c.CatchMouse();
}else if(a instanceof Bird){
Bird b = (Bird) a;
b.Sing();
}
}
}
多态在实际应用中的作用
降低程序的耦合度,提高程序的扩展力
public class Master{
public void feed(Dog d){}
public void feed(Cat c){}
}
以上的代码中表示:Master和Dog以及Cat的关系很紧密(耦合度高)。导致扩展力很差。
public class Master{
public void feed(Pet pet){
pet.eat();
}
}
以上的代表中表示:Master和Dog以及Cat的关系就脱离了,Master关注的是Pet类。
这样Master和Dog以及Cat的耦合度就降低了,提高了软件的扩展性。
面向对象的三大特征:
封装、继承、多态
有了封装,有了这种整体的概念之后。
对象和对象之间产生了继承。
有了继承之后,才有了方法的覆盖和多态。
这里提到了一个软件开发原则:
七大原则最基本的原则:OCP(对扩展开放,对修改关闭)
目的是:降低程序耦合度,提高程序扩展力。
面向抽象编程,不建议面向具体编程。
package duotai;
public class Test04 {
public static void main(String[] args){
Master m = new Master();
Dog d = new Dog();
Cats c = new Cats();
YingWu y = new YingWu();
m.feed(d);
m.feed(c);
m.feed(y);
}
}
class Master{
public void feed(Pet p){
//编译的时候,编译器发现p是一个pet类,会去Pet类中找eat()方法,结果找到了,编译器通过
//运行的时候发现底层实际是Dog对象,就会自动调用Dog对象对应的eat方法上。
p.eat();
}
}
class Pet{
//这个方法可以不给具体的实现
public void eat(){
}
}
class Dog extends Pet{
public void eat(){
System.out.println("dog eat");
}
}
class Cats extends Pet{
public void eat(){
System.out.println("Cat eat");
}
}
class YingWu extends Pet{
public void eat(){
System.out.println("YingWu eat");
}
}
解释之前遗留的问题
私有方法无法覆盖。
方法覆盖只是针对于“实例方法”,“静态方法覆盖”没有意义。(这是因为方法覆盖通常和多态联合起来)
静态方法存在方法覆盖吗?
多态自然就和对象有联系,而静态方法的执行不需要对象,
所以一般情况下,我们会说静态方法不存在方法覆盖
总结两句话:
私有不能覆盖。
静态不谈覆盖。
在方法覆盖中,关于方法的返回值类型。
什么条件满足之后,会构成方法的覆盖呢?
1、发生具有继承关系的两个类之间。
2、父类中的方法和子类重写之后的方法:
具有相同的方法名、相同的形式参数列表、相同的返回值类型。
学习了多态机制之后:
“相同的返回值类型”可以修改一下吗?
对于返回值类型是基本数据类型来说,必须一致。
对于返回值类型是引用数据类型来说,重写之后返回值类型可以变的更小(但意义不大,实际开发中没人这样写。)。
super关键字
super是一个关键字,全部小写
super和this对比学
this:
this能出现在实例方法中和构造方法中
this语法是:“this.”、“this()”
this不能使用在静态方法中
this. 大部分情况下是可以省略的,在区分实例变量和局部变量的时候是不能省略的
this()只能出现在构造方法的第一行,通过当前的构造方法去调用“本类”中的其他方法,目的是:代码复用
super:
super能出现在实例方法中和构造方法中
super语法是:“super.”、“super()”
super不能使用在静态方法中
super. 大部分情况下是可以省略的,
super()只能出现在构造方法的第一行,通过当前的构造方法去调用“父类”中的其他方法,目的是:创建子类对象的时候先初始化父类型特征
super()
表示通过子类的构造方法调用父类的构造方法
模式现实世界中的这种场景:要想有儿子,需要现有父亲
super() 只能出现在构造方法第一行,通过当前的构造方法去调用“父类”中的构造方法,目的是:创建子类对象的时候,先初始化父类型特征。
重要的结论
当一个构造方法的第一行:
既没有this()又没有super()的话,默认会有一个super();
表示通过当前子类的构造方法调用父类的无参数构造方法
所以必须保证父类的无参数构造方法是存在的
this()和super()不能共存,他们只能是都能存在构造方法第一行
无论怎么折腾,父类的无参数构造方法一定会执行的
package Super;
/*
1. super是一个关键字,全部小写
2. super和this对比学
this:
this能出现在实例方法中和构造方法中
this语法是:“this.”、“this()”
this不能使用在静态方法中
this. 大部分情况下是可以省略的,在区分实例变量和局部变量的时候是不能省略的
this()只能出现在构造方法的第一行,通过当前的构造方法去调用“本类”中的其他方法,目的是:代码复用
super:
super能出现在实例方法中和构造方法中
super语法是:“super.”、“super()”
super不能使用在静态方法中
super. 大部分情况下是可以省略的,
super()只能出现在构造方法的第一行,通过当前的构造方法去调用“父类”中的其他方法,目的是:创建子类对象的时候先初始化父类型特征
3. super()
表示通过子类的构造方法调用父类的构造方法
模式现实世界中的这种场景:要想有儿子,需要现有父亲
4. 重要的结论
当一个构造方法的第一行:
既没有this()又没有super()的话,默认会有一个super();
表示通过当前子类的构造方法调用父类的无参数构造方法
所以必须保证父类的无参数构造方法是存在的
5. this()和super()不能共存,他们只能是都能存在构造方法第一行
6. 无论怎么折腾,父类的无参数构造方法一定会执行的
*/
public class Test01 {
public static void main(String[] args){
new B();
/*
A类的无参数构造方法实现
B类的无参数构造方法实现
*/
}
}
class A{
int i;
public A(int i) {
System.out.println("A类的有参数构造方法实现");
}
public A() {
System.out.println("A类的无参数构造方法实现");
}
/*
一个类如果没有手动提供任何构造方法,系统会默认提供一个无参构造方法
*/
}
class B extends A{
public B() {
//调用父类中有参数的构造方法
//super();//自动有的
/*
A类的无参数构造方法实现
B类的无参数构造方法实现
*/
this("123");
/*
A类的无参数构造方法实现
B类的有参数构造方法实现
B类的无参数构造方法实现
*/
System.out.println("B类的无参数构造方法实现");
}
public B(String a){
//super(); 默认有的 先调用
/*
A类的无参数构造方法实现
B类的有参数构造方法实现
B类的无参数构造方法实现
*/
System.out.println("B类的有参数构造方法实现");
}
}
super和this的执行顺序
package Super;
public class Test02 {
public static void main(String[] args){
new E();
}
}
class C{
public C() {
System.out.println("C 执行");
}
}
class D extends C{
public D() {
System.out.println("D 执行");
}
public D(String name){
System.out.println("D name");
}
}
class E extends D {
public E() {
this("name");
System.out.println("E 执行");
}
public E(String name) {
this(name,20);
System.out.println("E name 执行");
}
public E(String name,int i) {
super(name);
System.out.println("E name i 执行");
}
}
//
C 执行
D name
E name i 执行
E name 执行
E 执行
super(实例参数)的使用时间
package Super;
/*
在恰当的时间使用super(实际参数列表):
*/
public class Test03 {
public static void main(String[] args){
CreditAccount ca1 = new CreditAccount();
System.out.println(ca1.getActno() + "," + ca1.getBalance() + "," + ca1.getCredit());
CreditAccount ca2 = new CreditAccount("a",123,111);
System.out.println(ca2.getActno() + "," + ca2.getBalance() + "," + ca2.getCredit());
}
}
class Account{
/*
私有的方法只能在本类中访问
*/
private String actno; //账号
private double balance; //余额
public Account() {
}
public Account(String actno, double balance) {
this.actno = actno;
this.balance = balance;
}
public String getActno() {
return actno;
}
public double getBalance() {
return balance;
}
public void setActno(String actno) {
this.actno = actno;
}
public void setBalance(double balance) {
this.balance = balance;
}
}
//其他类型的账户:信用卡账户
class CreditAccount extends Account{
private double credit;
public CreditAccount(String actno, double balance, double credit) {
/*
this.actno = actno;
this.balance = balance;
以上两行代码在恰当的位置,正好可以使用:super(actno)
通过子类的构造方法调用父类的构造方法
*/
super(actno,balance);
this.credit = credit;
}
public CreditAccount() {
}
public void setCredit(double credit) {
this.credit = credit;
}
public double getCredit() {
return credit;
}
}
null,0.0,0.0
a,123.0,111.0
super执行的内存图
注意:虽然调用构造方法,在构造方法执行的过程中还调用了一连串的父类的构造方法,父类构造方法又继续调用了它的父类构造方法,但是实际上对象只创建了一个

“super(实参)”到底是干嘛的?
super(实参):初始化当前对象的父类型特征,并不是创建新对象,实际上对象只能创建出一个
super关键字代表什么?
super关键字代表的就是“当前对象”的那部分父类型特征
例子
子类型和父类型无同名属性
package Super;
public class Test04 {
public static void main(String[] args){
Vip v = new Vip("z");
v.Shopping();
}
}
class Cumstor{
String name;
public Cumstor() {
}
public Cumstor(String name) {
this.name = name;
}
public void Shopping(){
}
}
class Vip extends Cumstor{
public Vip() {
}
public Vip(String name) {
super(name);
}
public void Shopping(){
System.out.println(this.name + "shopping");
System.out.println(super.name + "shopping");
System.out.println(name + "shopping");
}
}
zshopping
zshopping
zshopping

子类型和父类型有同名属性
package Super;
/*
this.和super.在大部分的情况下都是可以省略的
this.在区分局部变量和实例变量的时候不可以省略
super.父类中这个属性,子类中也有这个属性,想在子类型中访问父类型中的属性,就不可以省略
*/
public class Test05 {
public static void main(String[] args){
Vip v = new Vip("z");
v.Shopping();
}
}
class Cumstor{
String name;
public Cumstor() {
}
public Cumstor(String name) {
this.name = name;
}
public void Shopping(){
}
}
class Vip extends Cumstor{
//假设子类也有一个同名属性
String name;
public Vip() {
}
public Vip(String name) {
super(name);
}
//super和this都不能再静态方法中出现
public void Shopping(){
//this表示当前空间
System.out.println(this.name + "shopping");
//super表示当前对象的父类型特征(super是this指向的那个对象中的一块空间)
System.out.println(super.name + "shopping");
System.out.println(name + "shopping");
}
}
//
nullshopping
zshopping
nullshopping

super和this都不能再静态方法中出现
java如何区分子类属性和父类型属性
this.和super.在大部分的情况下都是可以省略的
this.在区分局部变量和实例变量的时候不可以省略
super.父类中这个属性,子类中也有这个属性,想在子类型中访问父类型中的属性,就不可以省略
super不能单独使用
super不是引用,super不保存内存地址,super也不指向任何对象
super只是代表当前对象内部的父类型特征
package Super;
/*
通过测试得出的结论。super不是引用,super不保存内存地址,super也不指向任何对象
super只是代表当前对象内部的父类型特征
*/
public class Test06 {
public void doSome(){
//输出引用的话,会自动调用引用的toSting方法
System.out.println(this);
//System.out.println(this.toString());
//错误需要'.'
System.out.println(super.);
}
public static void main(String[] args){
Test06 t = new Test06();
t.doSome();
}
}
package Super;
public class Test07 {
public static void main(String[] args){
Cat c = new Cat();
c.M();
}
}
class Animal{
public void move(){
System.out.println("Animal move");
}
}
class Cat extends Animal{
public void move(){
System.out.println("Cat move");
}
public void M(){
this.move();
move();
super.move();
}
}
- super.属性 - 访问父类的属性
- super.方法名(实参) - 访问父类的方法
- super.(实参) - 访问父类的构造方法
super执行的内存图
注意:虽然调用构造方法,在构造方法执行的过程中还调用了一连串的父类的构造方法,父类构造方法又继续调用了它的父类构造方法,但是实际上对象只创建了一个
[外链图片转存中…(img-L7U7fMuh-1634040754692)]
“super(实参)”到底是干嘛的?
super(实参):初始化当前对象的父类型特征,并不是创建新对象,实际上对象只能创建出一个
super关键字代表什么?
super关键字代表的就是“当前对象”的那部分父类型特征
例子
子类型和父类型无同名属性
package Super;
public class Test04 {
public static void main(String[] args){
Vip v = new Vip("z");
v.Shopping();
}
}
class Cumstor{
String name;
public Cumstor() {
}
public Cumstor(String name) {
this.name = name;
}
public void Shopping(){
}
}
class Vip extends Cumstor{
public Vip() {
}
public Vip(String name) {
super(name);
}
public void Shopping(){
System.out.println(this.name + "shopping");
System.out.println(super.name + "shopping");
System.out.println(name + "shopping");
}
}
zshopping
zshopping
zshopping
[外链图片转存中…(img-0aZuJNxt-1634040754692)]
子类型和父类型有同名属性
package Super;
/*
this.和super.在大部分的情况下都是可以省略的
this.在区分局部变量和实例变量的时候不可以省略
super.父类中这个属性,子类中也有这个属性,想在子类型中访问父类型中的属性,就不可以省略
*/
public class Test05 {
public static void main(String[] args){
Vip v = new Vip("z");
v.Shopping();
}
}
class Cumstor{
String name;
public Cumstor() {
}
public Cumstor(String name) {
this.name = name;
}
public void Shopping(){
}
}
class Vip extends Cumstor{
//假设子类也有一个同名属性
String name;
public Vip() {
}
public Vip(String name) {
super(name);
}
//super和this都不能再静态方法中出现
public void Shopping(){
//this表示当前空间
System.out.println(this.name + "shopping");
//super表示当前对象的父类型特征(super是this指向的那个对象中的一块空间)
System.out.println(super.name + "shopping");
System.out.println(name + "shopping");
}
}
//
nullshopping
zshopping
nullshopping
[外链图片转存中…(img-LhaJ8N99-1634040754693)]
super和this都不能再静态方法中出现
java如何区分子类属性和父类型属性
this.和super.在大部分的情况下都是可以省略的
this.在区分局部变量和实例变量的时候不可以省略
super.父类中这个属性,子类中也有这个属性,想在子类型中访问父类型中的属性,就不可以省略
super不能单独使用
super不是引用,super不保存内存地址,super也不指向任何对象
super只是代表当前对象内部的父类型特征
package Super;
/*
通过测试得出的结论。super不是引用,super不保存内存地址,super也不指向任何对象
super只是代表当前对象内部的父类型特征
*/
public class Test06 {
public void doSome(){
//输出引用的话,会自动调用引用的toSting方法
System.out.println(this);
//System.out.println(this.toString());
//错误需要'.'
System.out.println(super.);
}
public static void main(String[] args){
Test06 t = new Test06();
t.doSome();
}
}
package Super;
public class Test07 {
public static void main(String[] args){
Cat c = new Cat();
c.M();
}
}
class Animal{
public void move(){
System.out.println("Animal move");
}
}
class Cat extends Animal{
public void move(){
System.out.println("Cat move");
}
public void M(){
this.move();
move();
super.move();
}
}
- super.属性 - 访问父类的属性
- super.方法名(实参) - 访问父类的方法
- super.(实参) - 访问父类的构造方法

1501

被折叠的 条评论
为什么被折叠?



