/**java中数据类型的分类
* 基本数据类型
* 数值型数据类型
* byte 8bit 1bit byte型 就是一个格子,不是里面放的东西
* short 2byte 16bit 16位 短整型
* int 4byte 32bit 32位 整型
* long 8byte 64bit 64位 长整型
*
* 浮点型:小数型
字符型
* 布尔型
* 每个数据类型都对应了其划分的内存空间的大小;
* 先指定数据类型,即是先划定内存空间的大小;
* 赋值,把一定大小的数据存储到内存当中去;
* 如果数据大小超过了其数据类型划分的内存空间大小,就会报错;
* 如果赋值的数据小于内存空间的大小,就会进行自动数据类型转换,具体就是在高位加0
* 引用数据类型
* DT = date type
* 把代码写得漂亮,比写得正确更重要
* java严格区分大小写
*
* vim:技巧
* 删除当前字符:x
* 更改当前字符:r
*
* 数据类型的转换
*/
/**
* 小数数据类型:浮点型和双精度浮点型
* float 4byte(不是4bit) 4*8bit= 32位
* double 8byte() = 8*8bit= 64位
*
* 重点一: 注意java中这些数据类型的默认值,int i = 123; 默认是整型
* 浮点: float 默认值是 double
* 数据类型的转换
* 强制数据类型转换:高精度向低精度数据类型转换的时候,会丢失精度(就是丢失位数),有可能会影响数据的正确性.所以,需要强制数据类型转换
* 自动数据类型转换,因为是向高位增加位数,不会丢失精度,也就不会影响数据的正确性.
*
* 本质:都是用不同的类型大小的内存空间去存储合适的值
*/
/** java的数据类型:
* byte
* short
* int
* long
*
* float
* double
* 字符型:char;
* 布尔型:boolean;
* char类型:char
* char是字符类型,java采用的unicode编码,2个直接,byte16位2^16-1 = 65535,通过内部机制一百多万个字符
* java中的字符,是用''来声明的
* 字符类型不是字符串类型.字符串""
*
* 1 字符是现实世界中,存在的文字,在和计算机的交互中,里面二进制是一一对应的,这个维护机制就是码表
* 字符-->整型-->二进制
* 'a' ---> 97 所有的编码体系
* 'A' ---> 65
* '48'
* ISO-8859-1 西欧字符集 latin-1
* gb2312 gbk gb18030
* unicode UTF-8,UTF-16,UTF-32
* 如果编码和解码如果不一致就会出现乱码
*/
/** 转义字符
* 回车怎么表示:
* 换行怎么表示:
* 转义字符:就是把没有特殊意义的普通字符转换成有特殊意义和功能的字符
* 还有就是把有特殊意义的字符,转换成没有特殊意义能够像屏幕直接输出的字符
* 没有特殊意义:就是可以想屏幕直接输出
* 在计算机中是怎么完成这个转换的
* :制表符
*
: return
*
: newline新换一行
* s:空格
* " 输出一个双引号
*/
/**
* 基本数据类型的转换:
* 1 8种基本数据类型除了boolean之外,别的都可以互相转换
* 2 小容量数据类型向大容量转换,就是自动转换
* byte short int long float double char
* byte short int char这四种数据类型进行混合运算的时候,他们是全部都先转换成int类型再进行运算
* byte + short = short int
* 当多种数据类型进行混合运算的时候,都是先转化成最大的那一个,然后在进行运算
* byte + short + long = long
* 3 大容量数据类型向小容量数据类型转换,就必须强制转换
* 4 强制数据类型转换需要加强制转换符
* 编译的时候是能够通过的,但是这样会有损失数据的精度.谨慎使用
* 5 如果整数没有超过byte,short,char的取值范围的话,可以直接将这个整数赋值给byte,short,char
* long in = 1000 把数据传唤成L赋值给in
* byte b = int
*
* boolean不参与数据类型的转换
*
* 计算机领域就两个重点或者难点:
* 变量名命名和缓存
* 命名空间--名字空间--包机制--->防止变量名重复
*/
/**
* java中的布尔型值:
* 1 不是bool,是boolean
* 2 boolean只有两个值:true/false;和别的没关系.0,大于0
* 3 boolean用在条件语句中还就是逻辑运算中
* 3 boolean类型不能参与数据类型转换
* 现实生活和编程的接口:
* 数据类型,用数据的方式模拟了现实世界的一切
* 条件语句控制: 如果....就...要不然
*/
/**
* 1 java中的常量是没有关键字的:
* 别的const是关键字,表示在一个软件的生命周期中,值都不会改变的一个量
* 但是在java中,const是个保留字,就是说这个字我保留还没用
* 但是在编程实践中,java程序员常常把字面量,就是直接被操作的一个数据,没有名字的一个数据,叫做常量
* 常量/字面量的本质:某个数据类型的数据,直接被操作
* 整型:100;
* 浮点型:9.9
* 布尔型:true/false;
* 字符型:'中'
* 字符串型:"abc"
* 因为在内存中这些数据是占用内存空间的;占用的大小,编译器会根据数据的大小来互粉.
* 字面量在内存中无法重复利用
*/
/**
* java中的变量:
* 变量:就是一个有名字的内存空间;不是指里面的值,指的是里面装数据的格子
* 1 变量的声明:
* 数据类型 变量名;//数据类型就是划分内存空间的标准;变量名是给这个划出的内存空间取了个名字,以便重复使用
* 2 变量的赋值:
* 变量名 = 100;//就是把东西装到那个格子里面去
* 3 平常说的变量:
* 一个有名字,规定了大小的内存空间及其里面的值
*/
/** 变量的作用域:地盘,范围
* 局部变量:在一个方法当中声明的变量,叫局部变量
*
* 变量的作用范围,不会超出离他最近的那个外部大括号,但是和他平级的大括内部,他仍然起作用
*/
/**
* 什么叫变量的声明:就是给某个指定大小的内存空间取名
* 变量的分类:
* 局部变量:是指声明在某个方法或者循环体内部,某个{ } 内部的变量,他的范围不能超出这个大括号
* 成员变量:成员是声明在类体中,只要在类的任何位置都可以访问
*
* 程序的执行顺序:从上往下,从左往执行
* gg:光标跳转到文件的开头
* G:光标跳转到文件的末尾
* =G:当前行到某位的内容进行格式化,自动对齐
* ==:是把当前行自动缩进
*/
/** 字符串连接符
* + 只用字符串链接符的话,首先会把所有的数据类型都转换成字符串类型,然后链接.PHP中是一个'.'
* 运算的结果,也还是字符串类型
*/
/** 运算符,又叫操作符;00010101--->ASM
* 算术运算符:四则混合运算
* +, -,*,/,% 基本运算符
* ++,--
* ++i,i++的区别
*/
/** 关系运算符
* >,>=,<,<=,==,!=
* 当我是基本数据类型的时候,判断两边相等,用这个就是判断他们的值大小是否相等
* 但是是引用数据类型的时候,我们用个判断的就是他们的内存地址是否相等
* 注意和我们显示生活中的等号区分开来,=是赋值,不是等于判断,==
* 运算符有优先级别,但是不要记,用括号就可以了
*/
/** java的逻辑运算符
* & 逻辑与,两边都是true,结果才为true
* | 逻辑或,两边一个true,结果就为true
* ! 逻辑非,取反
* ^ 逻辑疑惑,两边不一样就为真
*
* && 短路&& 和逻辑& 一样,但是如果第一个为假,后面不论真假,结果都是假.所以啊,后面就没有进行判断,直接为假
* || 短路 || 和逻辑| 一样,一个为真的话,就全部为真,如果第一个为真,第二个就不需要判断了,结果也为真,后面的就不会被执行
*
* 布尔运算符,逻辑运算符,两边的算子必须是布尔类型,结果必须是true/false.没有是1,0这一说
* 5>3 | 6<4 false
*/
/**
* 基本赋值运算符 =
* 扩展运算符:
* +=,-=,*=,/=,%=。
* i += 10; i = i + 10;
* i +=1; i = i+1; i = ++i;三者等价
* 在运算过程当中,java会先判断数据类型,会先所有的数据类型转化成精度最大的那个数据类型,然后在进行运算
* 也就是说,运算符不会去改变数据的数据类型,也就是说不会去改变他们内存空间的大小;/
*/
/** 字符串连接符
* + 只用字符串链接符的话,首先会把所有的数据类型都转换成字符串类型,然后链接.PHP中是一个'.'
* 运算的结果,也还是字符串类型
*/
/** 三元运算符
* 条件判别式(结果是真/假) ? 结果为真执行语句:假执行的语句
*/
/**
* for循环
* for(表达式1;表达式2;表达式3){
* java语句;
* }
*
* 表达式1是初始化表达式,最先执行,只执行一次.
* 表达式2 boolean类型表达式
*
* for循环开始执行,这个相当于初始化这个计数器
* 进而判断表达式2的结果,如果是true,则执行费java语句.
* 在执行表达式3,然后在回去判断表达式2的结果,直到表达式2的结果是false,则for循环结束
*/
/** for循环嵌套.就是模拟一张表
* 所有的循环和控制语句都可以嵌套使用
*/
public class For02{
public static void main(String[] args){
for(int i=0; i < 5; ++i){
for(int j=0; j < 5; ++j){
System.out.print( i+""+j + " " );
}
System.out.println( );
}
}
}
public class For03{//使用for循环嵌套实现99乘法表
public static void main(String[] args){
for(int i=1; i <=9 ; ++i){
for(int j=1; j<=i; ++j){
System.out.print( i +"*"+ j + "=" + (i*j) + " " );
}
System.out.println( );
}
}
}
/** 1-100 所有奇数的和
* :申明一个变量:int sum
* :for()
* 先判断是不是奇数
* x%2 = 1
*/
public class For04{
public static void main(String[] args){
/*
int sum = 0;
for(int i=0; i <=100 ; ++i){
if (i%2 == 1) {
sum += i;
}
}
System.out.println( sum );
*/
int sum = 0;
for(int i=1; i <=99 ; i+=2){
sum +=i;
}
System.out.println( sum );
}
}
/** 1 方法的定义:
* [方法修饰符列表] 方法的返回值类型 方法名(参数1,参数2,参数3...){
* 要运行java语句;
* }
* 2 方法修饰符列表:public,protected,private,default;这个是为了数据的安全进行的而一个封装
* 3 返回值类型:11种数据类型中的任意一种,如果没有返回值类型,void
* 4 方法名:只要是合法的标示符即可;
* 5 方法的形参列表,多个都好隔开,形参也是需要指明数据类型
* 形参: 是指在函数/方法的定义处,摆着的那些参数;形参就是个变量,数据类型,变量名.多个形参之间有,隔开
* 实参:是指在函数的调用处,传入的那个参数;
* 6 在方法体内部,如果有返回值,就用return语句;return具有结束函数的作用.一碰到renturn函数就不在执行
*
* 程序语句执行的顺序问题:
* 默认的是从上到下,从左往右执行
* 但是在面向对象和方法编程过程当中,由于方法函数的出现,就可以先自行函数内部代码,然后在往后,怎么排列怎么执行
*
* 方法/函数:在定义的时候也就是在函数代码的地方是不执行的,是在调用的地方才执行
* 什么叫调用:就是方法名,然后传入实际参数,然后接受函数执行完以后返回的数据
* 面向对象中,方法的调用有两种: 第一种是new一个对象,然后把对象赋值给一个变量,然后
* 数据类型 变量名 = 对象变量名.方法名(实际参数列表);
* 类名.方法名();
*
* 尽管类和方法某种程度的改变了执行顺序,但是程序任然是从上到下,从左往右执行的
*/
/** 函数/方法:面向过程的语言当中,叫函数.在面向对象的编程语言当中呢,叫方法.
* 函数:是指一段有名字的代码段.
* 为什么有函数:为了代码的重用
*
* 有了方法开始,程序员就被分为了两类:
* 第一类是:客户端程序员
* 以main()为中心,写业务逻辑的实现的程序员
* 第二类是:类库类程序员
* 这个是以写方法或和类库位置的程序员,主要是以实现某种特定方向的功能为主
*/
/** 方法的分类
* java有两种方法:
* 成员方法:对象名.方法名();
* 静态方法: 类名.方法名();
* 如果你是在本类中访问自己的方法,就不需要加前缀:对象名/类名
*
* 程序执行过程当中,可以这么说:一直是在main()方法中执行,第一条语句就是程序的入口
*
* 方法不调用不执行,返回值返回到方法调用处
*
* 客户端程序员类库端程序员的概念
*
* 计算机世界中就两个重点和难点:命名和缓存
*/
/** 方法的调用和回调函数
* 传地址传值
* 回调:回调不可能理解递归
* 递归:递归和迭代是等价吗?
*
* 客户端程序员和类库类程序员
* main();
* 进入到类库类,非main()的编码
*
* 方法不调用不执行,把返回值返回到函数调用处!!!
*/
/** 方法的调用和回调函数
* 传地址传值
* 回调:回调不可能理解递归
* 递归:递归和迭代是等价吗?
*
* 客户端程序员和类库类程序员
* main();
* 进入到类库类,非main()的编码
*
* 方法不调用不执行,把返回值返回到函数调用处!!!
*/
/**
* return 结束函数的执行,但是不返回任何值
*
* 怎么区分两个函数/方法是不同的方法
* 实际参数列表和形式参数列表长度不同
*/
/**
* 方法的重载:overload
* 方法的重载是指在同一个类中有多个同名的方法,这些方法是通过参数列表来区分
* [修饰符列表] 返回值 方法名 (参数列表){
* 语句;
* }
*
* 为什么方法名相同:因为,同样一个功能会有很多不同的实现;那什么代表了函数或者方法的功能呢?
* function:功能
* 返回值:代表了当前函数或者方法的功能
*
* 参数列表怎么区分:
* 1 参数列表里面的参数个数不同
* 2 参数列表的参数数据类型不同
* 原生的是用方法名区分不同的方法
*/
/** 方法的重载
* 同一个类中,方法名相同,参数列表不同的方法叫做方法的重载
* :个数,数据类型不同
* 作用:1 降低了客户端程序员的记忆成本
* 2 让代码更好看
* 3 调用方法,同一个功能,调用同一个方法名
* --------------
* 注意和后面要学的,类的继承中的 覆盖/重写的区别
*/
/**
* 方法执行的流程
* 方法指向的原理:(方法调用才执行,不调用不执行)
* 什么情况下叫方法被调用?
* 方法被调用的时候,都发生了哪些事情?都内存中都有什么变化?
*
* 什么叫栈:栈是一种数据结构;什么是数据结构?为什么有数据结构?
* 数据结构:要保持数据的有序化.
* 为什么有数据结构?
* 因为我们电脑保存数据的方式,都是一维线性的,但是我们生活中,保存数据的方式都是二维的,我们要用一维的空间去模拟二维空间数据的保存,所以我们有数据结构这方法去保持数据的有序性
*
* 栈:就是一个弹夹,桶,薯片盒子,就是一种先进后出的数据结构
* 方法在调用的时候,是在栈里面划分一片内存空间,jvm有一篇内存就是栈内存
* 方法的调用:从内存的数据结构逻辑来讲就是"压栈"
* 方法的结束:从内存的数据结构来讲,就是'弹栈';
*/
/** 关于方法的递归调用
* 1 方法的递归调用,就是方法自己调用自己
* 2 下面这些程序,本质就是没有结束的那个条件,所以会一直压栈,没有弹栈,最终会导致内存溢出.
*
* 递归和循环是等价的吗? 是,本质上就是等价,只不过是代码循环级别的差异
* 递归是一种内循环.
* 无论迭代还是递归,都必须满足三个条件:
* 1 循环或者递归的起始条件
* 2 循环或者递归的终止条件
* 3 自身调用的条件
*/
/** 不使用递归实现1-N的求和
*
* 1 class 外面不能有代码
* 为什么? 每一个class都会单独的编译成一个class,写在class外面的话,编译肯定过不了
* 2 变量声明
* 只要你声明了数据类型,然后指定了名字
* int i,int i
* 3 什么叫函数的声明,什么叫函数的调用
* 函数不调用不执行,调用以后才执行
* 4 函数的返回值
* return sum;
* return 和void:有杀父之仇,不能同时在一个方法里面
* 函数的返回需要你去接收,不接受没办法使用
*/
public class Recursion02{
public static void main(String[] args){
int i = 5;
int k = sumN(i);
System.out.println( k );
}
public static int sumN(int n){
int sum = 0;//变量声明的规则:1 大小写字母,数字,下划线,$ 2 不要使用关键字
for(int i=0; i <= n; ++i){
sum += i;
}
return sum;
}
}
/**
*
* 使用递归计算 1-N的和(1),1-N奇数的(357),(2468)
* 循环:
* 起始点:N
* 结束条件:1
* 步长1
*
*/
public class Recursion03{
public static void main(String[] args){
int n = 5;
int reVal = sumN(5);//4+3+2+1
System.out.println( reVal );
}
/**
* 1-N的和
* 1 + 2 + 3 + 4 +...+ N
*/
public static int sumN(int n){
if (n == 1) {
return 1;//终止函数运行,每个函数的调用是不是都对应一个栈帧
//{}:就代表一个命名命名空间,一个命名空间就代表一个内存段,这里,也即是一个栈帧
}else{
return n + sumN(n-1);//函数的值返回在他的调用处
}
}
}
/** 传值和传引用:
* 什么是传值:就是把数据的值复制一份给人家传过去
* 传引用:就是把这个数据的内存地址复制一份传过去
*/
/**
* 面向对象本质是数据的封装:
* 封装的是什么数据? 就是那8种基本数据类型.
* byte short int long float double char boolean
*/
/** 在类的定义中,用this关键字表示该类创建的某一个对象
* this是jvm系统写进到到我们的代码中,用于表示某个类实例化以后的对象,本质保存了当前这个对象的堆内存地址.这个时候,this就可以代表当前对象了
*
* 1 当必须指出当前调用的方法的对象是谁时需要使用this
* 2 用this处理方法中成员变量和参数变量(参变量),重名的情况
* 3 this 就是一个变量,他的值是当前类文件创建的那个对象的引用.是在哪个对象中,就代表那个对象.
*
* 只要记住:this保存了当前对象的地址,并且在内存中默认第一个就是
*
*
* 客户端类:客户端类,不能够new,即是不能够实例化对象;包含main()方法的类也是可以new,可以实例化对象的,也还是可以在堆内存中被栈帧的方法调用的
* 类库类
*
* 构造函数/构造方法:就是我们new时候调用的那个方法;
* 方法的重载:就是在同一个类中,方法名相同,而参数列表不同的时候,叫做方法的重载
*
* [修饰符列表] 返回值 方法名(参数列表){
* 方法体;
* }
*
* 方法的定义和执行的问题:不是在一起,方法的执行永远在栈内存当中,每一个方法执行的都会开辟一个独立的栈帧,从而保证了每个方法里面的变量都是这个方法(栈帧)的局部变量
*
* 怎么区分两个方法是不同的方法?
* 为什么需要区分?
* 方法的重载,就是说方法同名,在同名的情况下怎么去区分两个方法是否相同?
* 参数列表(参数个数不同,参数数据类型不同)不同两个同名的方法就是不同的方法,如果同名参数列表相同就是方法重复
* 在重载中的方法调用:方法名+参数列表然后去匹配对应的方法
*
* 这段小代码的难点分析
* 1 客户端和类库端写在一起了,new的是自己,怎么new的,调用自己的构造方法
* 2 内存分析图中的流程不清晰
* 3 新的概念比较多:this 构造函数
* 4 方法的定义和执行的区别
* 5 方法的重载,方法重载的调用
*
* 通过程序执行来看
* 局部变量,就是栈帧内部变量
* 成员变量,就是值堆内存中的变量
* 静态变量,就是值静态数据区中的变量
*/
/** this 关键字
* 1 this是什么?
* this是一个引用类型
* 在堆中的每一个java对象都有this;
* this保存的内存地址是自己的内存地址(指向自己)
* 2 this 用在哪些地方
* 1 用成员方法中
* 2 用在构造方法中
* 语法 this(实参);
* 可以通过一个构造方法调用另外一个构造方法
* 目的:
* 代码重用
* this(实参);必须出现在构造方法的第一行
* 构造方法:
* 构造方法:在类中用来创建类不同对象的方法,这个方法和类同名,默认是公共,静态,无返回值的方法;
* 当我们没有定义我们自己的构造方法的时候,jvm会么默认的给我们一个无参构造方法;
* 如果我们定义了无参构造方法,或者是别的构造方法,jvm就不会再为我们指定无参构造
*
* 当我们在实例化,new 某一个类的对象的时候,实际上我们是在调用他对应的构造方法;
*
* this用在构造方法中,1 表示这个类的对象,也就是把变量加个前缀,也就是表示这个变量所表示内存空间的位置在堆内存中
* 2 this();通过里面传递的参数表示该对象中别的构造方法
*/
/** this除了构造方法以外也能用在成员方法中,表示当前对象
*
* 但是可以用在静态方法中吗?
*
* 成员方法,会和成员变量一起被复制到堆内存中;
*
* this除了构造方法以外也能用在成员方法中,表示当前对象
*
* 但是可以用在静态方法中吗?
*
* 成员方法,会和成员变量一起被复制到堆内存中;
*
this可以用来区分成员变量和局部变
*
* this 不能用在静态方法中
* 什么是静态方法? 静态变量?
* 静态方法就是方法前面加了static关键字修饰的方法.加了静态关键字的方法在类实例化对象过程中不会复制到堆内存中的对象里面.
* 一般情况下,在类库类中,只要和某个特定对象无关是这个类所有对象都具备的动态属性,就声明为静态方法.
* 成员方法,就是没有加static关键字修饰的方法,他会被复制到堆内存的对象中.
*
* 总结一下:类里面都有什么东西了?类的构成.
*
* 成员变量:没有加static修饰的变量叫成员变量.就是类实例化对象以后,保存在堆内存中的对象.
* 用实例化后的对象的引用去访问
* 静态变量:加了static关键字的变量,不需要实例化,直接保存在静态数据区中的变量
* 不需要实例化,直接用类名就可以进行访问
*
* 成员方法:没有加static修饰的方法,会在类实例化对象后复制到堆内存中的方法
* 用实例化后的对象的引用去访问
* 静态方法:加了static修饰的方法,会一直保存在静态代码段中的方法.
* 不需要实例化,直接用类名就可以进行访问
*
* 局部变量:就是在栈帧内存保存的变量.
*
* 构造方法:用于构建当前类的对象的方法.不同的构造方法会构建具有不同成员属性的对象
* 构造方法名和类名相同,默认是公共,静态,没有返回值的
* 可以使用构造方法对对象的成员变量初始化赋值;
*
* this:
* 静态上下文:就是静态代码段和静态数据区构成的那个整体的内存空间
*
* 为什么对内存进行分段划分? 1 为了安全 2 为了便于管理
* 类的构成:
* 7 + 2
* 成员变量
* 静态变量
* 成员方法
* 静态方法
* 构造方法
* this
* 局部变量
* super
* 静态代码块
* 动态代码块
*
*
* 静态方法可以用对象名直接访问,尽管我们用的是对象名访问我们的静态方法,但是在编译的时候,编译器会把那个对象名转换成对应那个类名
*
* 静态代码块:静态语句块
* 在我们的jvm在从硬盘把类文件载入到内存中的时候就执行,并且只执行一次.为什么呢?因为在程序生命周期中,类只会加载一次.可以有多个,依次执行
* 有什么用? 性能测试,写日志
*
* 动态代码块
* 动态代码块是在我们实例化这个类的对象的时候,会最先运行的代码段.实例化这个类多少次,就运行多少次
* 性能监控和写日志
*
* 在实例化过程当中代码的执行顺序:(特指类库类和客户端类在一起的时候)
* 1 静态代码段
* 2 动态代码段
* 3 main()方法里面的代码
* 4 构造函数里面的代码
*
* 这个例子里面只需要理解静态代码段和动态代码段就可以了
*
*在实例化过程当中代码的执行顺序:(特指类库类和客户端类在一起的时候)
* 1 静态代码段----->找main()入口
* 2 main()方法先执行--->main()方法里面的代码
*
* 实例化对象的时候,谁先指向
* 动态代码段---->构造函数里面的代码
*
* 软件运行的生命周期:
* java 软件名
* 1 jvm请求操作系统找到对应的*.class类文件,如果这个类文件里面还有别的类的引用时候,就会递归去把所有的类一次性载入到内存静态代码段中.并且,如果里面包含静态代码段,就像按加载的顺序先后执行
* 2 载入完成以后,jvm虚拟机回去找main(),然后在栈内存中开辟一个main()方法栈帧
* 3 开辟栈帧完成后,按照从上到下的先后顺序执行里面的代码
* 4 如果里面有new 了别的对象,会(那4步)
* 5 所有方法的调用都必须在栈内存里面开辟栈帧然后递归执行
* 6 所有的方法执行完成以后,都会返回到main()方法,从某种程度上讲,一个程序就是在全部都是在main()中执行
* 7 main方法执行完成以后,整个程序执行结束,然后shutdown()操作系统退出jvm虚拟机
*/
/*
类{
//静态属性:通过 类名.属性名,引用名.属性名,来使用,但是即使使用引用名,底层也是类名.静态属性的值,一般由类库程序员定义好了
//1 静态变量
//2 静态方法
//成员属性:必须在对象的情况下才能使用,引用名.成员属性名
//3 成员变量
//4 成员方法
//5 构造方法:创建对象,客户端程序员给成员变量赋值;一般写在类库类中
//6 入口main方法:程序业务流的入口,客户端类必须有的方法;
//7 静态语句块:类加载的时候执行,且在程序的生命周期中只执行这一次
//8 实例化类的时候,在没有调用构造方法前执行一次,每一次实例化操作,都会执行一次
//9 this
空指针异常
}
*/
/**
* java 包机制(package):在java源文件头规定的编译后的类文件的文件路径.
* 也就是操作系统的文件管理系统的目录结构
* 为什么有包机制:这个是为了解决类文件命名冲突的问题.在不同的领域有很多的手段
* IP: 把根IP,把IP挨个分级
* MAC:
* 电话区号:
* 行政区规划:
* 类名:web.site
* web.site,通过包机制往前加前缀
* 具体操作是怎么用的:
* 每个公司一般都有一个独一无二域名,java就是把每个公司的域名到过作为包名使用
* 这样就保证了你的类名是独一无二
* 怎么用:包的声明:就是在源文件的第一行用package关键字来声明报名.
* 有包的类怎么用? 用import导入整个包文件或者按照严格的路径具体导入某一个类文件,在同一个包中的类文件不需要导入
* class文件的最上层包的父路径必须已经定位到操作系统的classpath中,因为你要用某个类,必须要先能找到
*
* 但是也可以不导包,但是呢,你需要写整个class的全路径
*
* javaSDK中要用到的包:
* java.lang.*;这是java的核心类包.
* java.awt.*.;这个javaGUI的一图形界面包
* java.applet:这个已经废弃,她的功能被javascript取代.
* java.net.*:网络编程包
* java.io.*:就是专门用于数据传输的包
* java.util.*:这个是常用的工具类包;日期,数学,日历,各个国家的货币处理都在里面
*
* 打包:java -cvf xx.java *.*;
* jar包可以作为classpath的路径直接使用
*/
/**
* 通过前面的review,我们总结了在单个类文件的内部组成和调用关系
* 我们写类的目的是为了代码复用
* 之前的复用方式是:类实例化对象,new;直接调用类的静态方法和静态变量.
* 今天我们进入类的复用的另外一种方式:继承 extends
*
* 引入继承的基本作用:代码重用,extends
* 继承的语法结构
* [修饰符列表] class 子类名 extends 父类名 {
* 类体;
* }
* java语言中,类和类之间只支持单继承.
* 一个类如果没有显式他继承其他类,则该类默认继承Objet,如果显示了呢?
* 任何类都有一个始祖类Object,所有类的超类
*
* 代码复用:
* 三种: 函数级别:调用
* 类级别:new 实例化
* extends 继承
*/
/**
* 一个类,叫根类,是所有类的始祖类.java.lang.Object;
*/
/**
* 1 构造方法是不能被继承的
* java语言中子类继承父类,会将父类中所有的数据,成员变量,静态变量,成员方法,静态方法,即使是private(可以被继承但是外部不能直接方法)
* 继承过来的属性和方法,在子类中就可以直接访问的.包括所有的属性和方法
*/
/**
* !类:可以被继承
* !成员变量:查(对象名.成员变量名),改(对象名.成员变量名=xxxx)
* !静态变量:查(类名.静态变量名/=,类名.成员变量??/=, 对象名.静态变量/=)
* !局部变量:可以在方法重新赋值
*
* !成员方法可以被重写:override/overwrite
* 静态方法:在继承过程当中,静态方法没变化
*
* 只要在相应前面加一个final
* final 成员变量的时候,这个成员变量就只会被赋值一次;
* final static 静态变量,这个时候这个变量就叫常量,常量的名字需要大写
* public static final int AAA = 1000;
* final int i = 100; 这种的就在整个方法的生命周期当中不会再被改变
*
* final public void 方法名(){} 方法在继承的时候不会再被改写
* 静态方法
* final class 类名;这个类不能再被继承
*/
/** final都是基本数据
* final修饰引用数据类型;引用的内存地址不能在改变;
* 但是这个内存地址指向的对象里面的数据是可以改变的
*/
/** 方法的覆盖/重写只存在在子类继承父类的这种情况中.
* 1 什么是方法的覆盖/重写?
* 当父类的方法无法满足子类中的业务需求的时候,可以在子类中把父类的方法按照需求重写.
* 2 子类如果重写了父类的方法客户端或者子类对象去调用该方法的时候,默认会去调用子类改写了以后的方法.
* 3 方法覆盖/重写的条件
* 1 必须是有继承关系的两个类之间
* 2 两个方法的方法名,返回值类型,参数列表必须相同.why?
* 方法名和参数列表要相同?如果你在子类中写了不同方法名,你自己定义一个方法.第二个,如果你写了同名但不同参,相当于你对父类的方法进行了重载.从这里看出,要更改父类的某个功能有几种方法(新定义,重载,重写)
* 返回值类型:就代表了函数的功能.这个是不可能变
* 3 重写的方法不能比原有的方法有更低的访问权限(子类不能降低父类对外提供的功能protected->private->default->public)
* 4 重写的方法不能比原有的方法更宽泛的异常(异常机制里面讲)(重写后只能错误越来越少,不能越来越多)
* 5 私有方法不能被覆盖/重写:多态--(那就是可以继承,私有变量也就可以被继承);
* 6 构造方法不能被覆盖/重写,是因为构造方法不能被继承.why什么不能被继承?
* 7 静态方法不能被覆盖/重写,为什么呢?
* 静态方法都是属于类级别,只要是这个类的子类或是对象,都应该具备这个属性,所以就不能被重写
* 8 覆盖/重写是指成员方法,和成员变量,静态方法无关.
* 继承最基本的作用是:代码重用,并且成员方法可以重写
*
* 什么时候用new,什么时候用继承;
* 继承相对实例化new,最大优势是可以对方法进行重写,从而带来最大功能性扩展
* new的优势,可以从外部对对象内部的数据改查.
*/
/**
* 私有的方法不能被覆盖/重写,即使在子类中重写了父类的私有方法,但是底层仍让会去调用父类未被重写的那个方法
*/
/** 静态方法不存在覆盖
* 因为覆盖是发生在对象的成员方法中,也就是发生在堆内存做中,但是静态方法都保存在静态代码段中的,所以不能发生覆盖.并且,并且静态方法如果用改类的实例化的对象去调用的话,底层仍然会用类名去调用静态方法;
*/
/** 方法的构成:
* [修饰符列表] 返回值数据类型 方法名 (参数列表){
* 运行体;
* return 返回值;
* }
* static
*
* 封装的级别.由内到外
* 这些权限修饰符,修饰的是谁?修饰的是要被操作那个数据或者方法或者类
* 他们会在哪里被访问,类
* public private protected default
* 当前包里面同一个类 可以 可以 可以 可以
*
* 当前包里面别的类 可以 不可以 可以 可以
*
* 别的包里面的类 可以 不可以 不可以 不可以(V)
*
* 从别的包继承关系的类中 可以 不可以 不可以 不可以(V)
* 从自己包继承关系的类中 可以 不可以 可以(V) 可以
*
* default 就是默认包内权限,只要是在一个包里面就都可以访问.但是出了这个包,处分你是那个公共的二类
*
* 面向对象思想的核心: 封装
* 通过public,private,protected,default对不同重要级别的数据进行分类按层级保护.
* 给不同的数据打标签
* 继承
* 多态
*
*
*
* 包的内部:各个类
*/
/** 面向对象思想的三个核心:
* 封装
* 继承
* 多态
*
* 封装:按照数据的不同重要性,给外部的访问不同的访问权限.
* 权限有几种:4种
* public
* 任何人在任何地方都可以来访问该数据
* 我盖了一个公园,就是人任何人都可以来玩
* protected
* 继承子类可以访问的权限,继承访问权限
* (相当于我再有很多的房产的房产证,我和儿子共同管制)
*
* private
* 类的访问权限,只能是自己类的内部可以访问,继承的类都不可以
* (只有我可以,任何人都不行,相当于我保险柜中,瑞士银行的密码,我儿子都不行)
* default
* 包权限:自己包中的类都可以访问,出了当前的包就不可以
* 我有很多地,我是个大地主,然后这些地,我就让家族的人一起管理.比如,我的地要收租,就让家族的人去收.
*
* 但是,我们定义的这些数据,是真的不让外部访问吗?不是,还是要让外部访问的.
* 我们的目的是自主,可控.
* 我们还让外部可以访问我们,但是,你不能直接访问我的裸数据.
* 我会让你访问某一个方法,然后我在方法中对你行为进行控制
*
*/
/**
* 什么是单例模式:
* 在对外提供服务的程序中,为了节约内存的考虑,在面向客户的那个程序对象,我们只创建一个实例对象
* 实现单例模式:
* 实现返利模式的步骤:
* 1 构造方法是有化,让外部无法直接创建对象
* 2 创建一个公共的静态的实例化当前类对象的方法
* 3 提供一个当前类型的静态变量
* 根据对象的创建时机不同
* 在类加载是就立马创建这个对象:饿汉模式
* 在用到对象的时候,才创建这个对象:懒汉模式
*/
/** super关键字用在构造方法中:
* 语法:super(参数列表);对应了父类中相应的构造方法
* 作用:通过子类的构造方法调用父类的构造方法
* 语法规则:
* 如果一个构造方法中,第一行没有this();也没有显式的去调用super();系统就会默认的调用父类的无参构造super();
* 注意要点: super(...)只能放在构造方法的第一行
* 我们之前说过:this(...)也只能放在构造方法的第一行
* super(...)和this(...)不能共存.
* super(...)调用了父类中的构造方法,但是并不会创建父类对象.
* java语言中只要创建java对象,那么Object中的无参构造方法就一定会执行.
*/
/** this 是指当前对象的内存地址
* super:官方说法是,super 对父类动态属性的一个抽象,但是我们就认为我们在实例化子类的对象的时候,同步的实例化了父类的对象
*
* 1 super 不是引用类型,super中存储的不是内存地址,super指向的不是父类的对象
* 2 super代表的是当前类的对象中的父类型特征
* 3 为什么使用super?
* 子类和父类都有某个数据,例如:子类和父类都有name属性,当在子类对象中需要访问父类name属性的时候,就需要在前面加个super.name
* 4 super可以用在什么地方?
* 1 super可以用在成员方法中.不能用在静态方法中
* 2 super可以用在构造方法中,表示对父类构造方法的调用.
*/
/* 概念的层级
* 初始级别
* 数据类型: 1 在内存里面开辟一个对应空间,不同数据类型有不同.
* 2 变量:标识符的命名规则
* 3 初始化,赋值,初始化赋值.
* 语句:对应的是一个cpu操作.一个语句就对应一条cpu.
* 第一层级的封装
* 函数: 1 函数的定义:有名字,对应一定功能的代码段.
* 2 函数的结构
* [修饰符列表] 返回值 函数名(参数列表){
* 运行体;
* return 返回值;//结束函数运行
* }
* 3 函数的声明,函数重载(方法的重载)
* 4 函数的调用,函数不调用,不执行;执行完成后会返回到被调用处.函数一调用肯定就会在栈内存中开辟栈帧,函数结束return,运行完结束都是弹栈.
*
* 第二个层级封装
* 类和对象
* 类:就是我们在项目中抽象出来可以反复使用数据模型.作用是生成不同类型的对象.对象是什么呢?就是同一个数据模型处理不同数据的一个实例.
* 类的构成:
* 静态变量
成员变量
* (局部变量)
*
* 静态方法
* 成员方法
* 构造方法
*
* (this)
* (super)
*
* 静态代码段
* 动态代码段
* 对象构成:
* 成员变量
* 成员方法
* this
* super
* 内存:
* 静态代码段
* 静态数据区
* 栈内存
* 堆内存
*
* 软件的生命周期
* 编译期
* javac 检查数据类型,检查语法结构
* 运行期
* java
* 加载:运行的是--静态代码段
* 动态记载:动态加载,这里的开始并不会把全部的相关的类加载进来
* 静态加载:一次性把相关的类全部载入到静态代码段中(不是这样的)
* 运行的第一步:jvm调用:main().(和动态代码段谁先运行的问题),因为main()是个静态方法,不需要实例化类,可以直接使用,所以,main()一定是先执行的.
* 运行的第二步:按顺序的执行main()方法中的代码.
* 1 如果是赋值操作
* 2 方法调用,传值和传引用
* 传值:里面传的是基本数据类型
* 传引用:传的是引用数据类型实例化后的那个对象的地址.
* 压栈和弹栈
* 3 类的实例化
* 引用数据类型的类名 变量名 = new 对象的构造方法();
* (1) 从硬盘当中加载要实例化的那个类(这就是动态加载)
* 1 调用构造方法,构造方法的重载
* 构造方法默认是公共,静态,无返回值的
* 2 new开辟空间,this赋值
* 3 复制:成员变量,成员方法
* 4 赋值:new出内存地址给了变量
* 从上面可以看出,程序的运行基本上都是在main方法中运行的
* main()结束,程序就结束.
* 第三个层级的封装:包机制package
* 包机制的目的是为了保证我们声明的类的类名具有全球唯一性.也就是别的语言中命名空间概念.
* 方法是,在每个源文件的加上一个唯一的类名调用前缀,他对应了类文件在文件系统中的目录层次结构,同时也保证了在调用过程中程序名的唯一性
* 怎么保证唯一性,都还是用一个网站的域名来做前缀,因为一个网站的域名肯定是全球唯一的.
* 在调用某个类的时候,可以直接使用类名的全名调用,也可以用import关键字调如某个类.
* 类名的层级结构中,必须保证其根目录的父目录在操作系统中注册(CLASSPATH)
*/
/** 面向对象三大核心思想:
* 封装:
* 继承:
* 多态:父类的引用指向的子类的对象.
* 就是我们在栈内存中开辟空间保存的内存地址的类型是父类型,但是呢真实的内存地址却是这个类的子类的一个对象的内存地址.
*
* 多态的本质是个什么东东?数据类型转换.
* 回忆一下基本数据类型的转换:
* byte short int long
* float double
* char
* boolean
* 低精度向高精度转换,是自动类型转换
* byte-->short-->int->long
* float->double
* char-->int
* 从高精度向低精度转换是强制类型转换.
* int i = (int)2.0;
* --以上是基本数据类型的数据转换
* --引用数据雷响可以相互转换吗?
* 什么时候进行引用数据类型的转换?
* 1 在继承关系体系中,可以在父类和子类之间进行转换.
*
* 如果是子类变成父类:子-->父,前面声明的时候是个父类型,后面实例化的时候是个子类型.父类引用指向子类的对象,我们把它叫做向上转型.
* 这样的话,子类就变成了父类,那个父类的引用就只能够调用子类中重写了的父类的方法.不能调用子类特有的方法和属性.这个就是丢失了子类的方法和属性
*
* 在多态当中,只涉及成员方法
* 成员变量:不涉及
* 构造方法呢:根本不继承,所以也不涉及,因为构造方法是用来创建每个类对应的实例化对象.
* 静态方法呢:静态方法也不继承,静态方法是属于整个类型.
* 静态变量;
*
* java语言中的向上转型和向下转型
* 1 向上转型(upcasting): 子-->父
* 2 向下转型(downcasting):父-->子
* 具体语法:
* Animal a = new Cat();
* Cat c = new Animal();//会报错
*/
/** 多态:父类的引用指向子类的对象
* 这里注意点:客户端类和类库类的区别
* 客户端类:包含有main方法的类叫客户端类
* 首先,在类库类中有继承关系,并且子类重写了父类的方法
* 然后在客户端类中,用父类的引用指向子类的对象
*/
public class Poly03{
public static void main(String[] args){
//Animal c_1 = new Animal();
//多态:父类引用指向子类对象:子类型对象的引用的数据类型(内存中按照父类型的类型划分一个类存空间,但是里面保存指针指向的是子类型的对象)
Animal a_1 = new Cat();
//程序在编译器阶段,a_1是animal;所以编译阶段a_1绑定的animal类型中的eat()方法;程序在运行时阶段,堆中的对象实际上是cat类型的对象,而Cat中已经重写了eat()方法.所以程序在运行绑定的是Cat中的eat()方法;--这就是动态绑定
//向上转型为什么可以自动转换:因为扩展类肯定包含基类的接口,向上转型,就可以通过父类的方法直接调用子类的方法.但是会导致子类特有的方法和属性的丢失
//多态的基本点:就是接口覆盖/重写.成员方法绑定是动态的,编译是不进行检查;在执行的时候,初始化完成后,再进行绑定
//关键点:客户端程序员,实例对象以后(实例化),我们只需要一直调用基类的方法即可(这个也是后面学习接口的基本原理)
//但是在执行的时候却会调用子类对象的方法;所以我们的客户端流程类和类库端的扩展类(子类)完全没有关系(绝对解耦)
a_1.eat();
//程序的开发流程:确定需求,项目设计,编码,测试,交付/部署,维护/升级
//向上转型是不是会丢子类的方法和属性,有时候我们需要调用子类中特有的接口(public方法:叫接口,包括成员属性对应的get和set方法);但是我们必须保证我们调用的子类型对象是正确;所以,java中默认所有的强制类型转换在编译期都会进行强制检查,报错;
//向下转型需要先向上转型;如果没有向上转型就不存在向下转型;
//这个主要用处在程序的分工和设计中:在编码中,我们就不需要去实例化子类;我们在程序设计阶段,在子类中尽量不会出现新的接口,所以向下转型很少出现,只不过是一种权宜之计.
//客户端调用类库类代码的两种方式:组合,直接new;继承(泛化)
//在实际编码中,尽量使用组合,不使用继承.继承的话,一般都是为了多态,一是为了扩展功能,二是为了分工
//在向下转型中,为了避免出错,我们会先判断一下类和对象之间的关系,继承和实例化:两个类之间是不是有继承关系,是不是子类的对象.
//这个时候我们就 父类引用 instanceof 子类对象名,判断当前这个父类的引用变量指向的那个子类对象,和我们要转化成的这个对象是不是同一个类实例化得来
//instanceof返回值类型是boolean类型,只要两个对象,得实例化类一直,就是真,要么为假
Animal a_2 = new Cat();//a_2对应内存中的地址是cat对象的地址
//声明一个Cat类型内存空间,并且赋值为null:伪代码
Cat c_1 = null;
if (a_2 instanceof Cat) {//检验a_2是不是实例化于Cat的对象
c_1 = (Cat)a_2;//向下转型为Cat的对象
}
c_1.eat();
}
}
/*
* 向上转型:
* 是父类引用指向子类对象
* 向下转型:
* 子类的引用指向父类的对象;是错误的
* 子类的引用指向的是子类的对象
*/
/** 什么叫抽象类: 就是用abstract关键字修饰的类叫抽象类
* 什么是抽象方法:没有方法体的方法叫抽象方法,连{ }都没有;一旦是抽象方法就必须在修饰符列表中加上abstract这个关键字修饰
* 含有抽象方法的类一定是抽象类,这是正确的.
* 抽象类一定含有抽象方法,这就不对
* 为什么没有抽象方法,还要写成抽象类呢?就是为了让该类不能实例化.
* 抽象方法的性质:
* 1 抽象方法不能被实例化(还有一种方法也不能让类实例化,构造方法私有化,private)
* 2 虽然抽象方法不能被实例化,但是仍然有构造方法,这个是让子类创建对象用的.(构造方法不能被继承,super())
* 3 抽象类中不一定有抽象方法,但是抽象方法必须写在抽象类中
* 4 抽象类的继承类,必须把所有的抽象方法实现(你全部要加上方法体)
* 如果没有全部实现,那么这个子类还是抽象类,还要加abstract关键字,还是不能直接被实例化
* 抽象类可以用final修饰吗?抽象类不能使用final修饰
* 抽象方法可以用final修饰吗?abstract和final不能同时出现.抽象方法不能用final修饰
*
* 抽象类和抽像方法一般用在类库类中
* 客户端类中,非要用abstract也可以,但是无影响
*/
/** 引用数据类型:
* 类,接口,数组
*
* 接口是一种引用数据类型,可以看做特殊的类,是java为了解决没有多重继承引起的功能弱的问题而设计,一个类只能有一个父类,但是可以实现多个接口
* 接口可以看做是只有抽象方法的类,但是声明的关键字由class变成了interface;子类也有extends变成了implements(实现),但是功能和意义是相同的.
*
* 1 定义接口的语法
* [修饰符列表] interface 接口名{}
* 2 接口中的内容:只能是常量(public static final 常量名)和抽象方法
* 3 抽象方法不需要abstract修饰,默认就是
* 4 接口可以看做是一个特殊的抽象类,就是完全抽象,没有普通方法
* 5 接口中没有构造方法,所以无法被实例化.
* 6 一个类可以实现多个接口,但是只能继承一个类
* 7 一个非抽象类实现接口,需要将接口中所有的方法都实现(重写/覆盖);
* 8 接口之间可以多继承,类只能单继承
*
* 一般情况下什么会被声明为接口:
* 对象某一方面的属性会被声明为接口***able
* 常量和静态数据的区别:
* 都是在类加载的时候初始化,但是静态数据可以多次赋值
* 但是常量在声明的时候同时赋值,赋值以后不能再次赋值,要不然就会报错,并且常量的值在软件的整个生命周期中都不会改变
* 命名方式:大写字母加下划线的形式命名
* 常量:必须使用 psf:public static final修饰,但是呢,在接口中可以省略;
*/
/**
* 客户业务接口
* 客户端调用就是这个接口
* 作用:
* 1 可以使项目分层,所有的层都面向接口开发,开发效率提高了(客户端和类库端都是去调用接口)
* 2 接口使代码和代码之间耦合度降低,就像内存和主板的关系,便得"可插拔"
* 可以随意切换
* 同样能完成这个功能的有接口和抽象类,选谁呢?
* 优先选择接口:接口之间可以实现多继承,并且,一个类也可以实现多个接口,并切不影响去继承一个别的类
/ava中如何比较两个字符串
*
* 在java中比较两个字符串是否一致,不能使用"==";
* 只能String类equals();
*
* //任何应用数据类型的比较都必须转化成基本数据类型的比较,除非就想知道他们的内存地址是否相等;因为 ==,!= 只能比较基本数据类型,当两个对象进行比较的时候,会比较他们的16进制的内存地址
*
* 当我们去比较的时候,肯定是拿对象某一方面的属性对应的值去比较.立马就变成了基本数据类型
*
* 所以说,面向对象就是一种基本数据封装的形式,所以我们比较的时候,最终都会转化成基本数据类型的比较
java.lang.Object;中的finalize()方法
* finalize()方法什么时候调用:
* 1 finalize每个java对象都有的方法
* 2 finalize()方法不需要程序员去显式的调用,有jvm自动调用
* 3 java对象如果没有更多的引用指向它,则该对象成为垃圾数据
* 等待垃圾回收器回收,垃圾回收期在回收这个java对象之前会自动调用该对象的finalize()
* 4 一调用finalize(),意味着对象马上要被回收,这个时候,程序员可以做一些必要的操作,比如日志记录,资源释放
一、继承关系/泛化关系 继承指的是一个类(称为子类、子接口)继承另外的一个类(称为父类、父接口)的功能,并可以增加它自己的新功能的能力。在Java中继承关系通过关键字 extends明确标识,在设计时一般没有争议性。在UML类图设计中,继承用一条带空心三角箭头的实线表示,从子类指向父类,或者子接口指向父接口。
二、实现关系 实现指的是一个class类实现interface接口(可以是多个)的功能,实现是类与接口之间最常见的关系。在Java中此类关系通过关键字 implements明确标识,在设计时一般没有争议性。在UML类图设计中,实现用一条带空心三角箭头的虚线表示,从类指向实现的接口。
三、依赖关系 (就是局部变量) 简单的理解,依赖就是一个类A使用到了另一个类B,而这种使用关系是具有偶然性的、临时性的、非常弱的,但是类B的变化会影响到类A。比如某人要过河,需 要借用一条船,此时人与船之间的关系就是依赖。表现在代码层面,为类B作为参数被类A在某个method方法中使用。在UML类图设计中,依赖关系用由类 A指向类B的带箭头虚线表示。
在程序的整个流程中(程序的生命周期),某个步骤,某几个有限的步骤,需要另外一个类的功能才能完成
比如在一个方法中调用了另外一个类
说白话:就是一个类的局部变量是另一个类的对象.
四、关联关系 (就是成员变量)关联体现的是两个类之间语义级别的一种强依赖关系,比如我和我的朋友,这种关系比依赖更强、不存在依赖关系的偶然性、关系也不是临时性的,一般是长期性 的,而且双方的关系一般是平等的。关联可以是单向、双向的。表现在代码层面,为被关联类B以类的属性形式出现在关联类A中,
ClassC cc = new ClassC()
ClassC.staticVar
全局变量,就是指他的成员变量,静态变量
也可能是关联类A引用了一个类 型为被关联类B的全局变量。在UML类图设计中,关联关系用由关联类A指向被关联类B的带箭头实线表示,在关联的两端可以标注关联双方的角色和多重性标 记。
就是一个类成员变量或者静态变量是另外一个类的引用或者是对象的引用
重点: 局部内部类访问局部变量的时候,局部变量必须使用final修饰符
什么是单例模式:
* 在对外提供服务的程序中,为了节约内存的考虑,在面向客户的那个程序对象,我们只创建一个实例对象
* 实现单例模式:
* 实现返利模式的步骤:
* 1 构造方法是有化,让外部无法直接创建对象
* 2 创建一个公共的静态的实例化当前类对象的方法
* 3 提供一个当前类型的静态变量
* 根据对象的创建时机不同
* 在类加载是就立马创建这个对象:饿汉模式
* 在用到对象的时候,才创建这个对象:懒汉模式
*/
public class Singleton01{//懒汉模式
//先把构造方法私有化,不要让别人直接创建对象
private Singleton01(){}
//创建一个静态变量,用于装载实例化后的那个对象的内存地址
private static Singleton01 s;
//对外提供一个公开的获取当前类对象的一个方法
public static Singleton01 getInstance(){
if (s == null) {
s = new Singleton01();
}
return s;
}
}