程序员,程序猿,程序媛,码农
———————————————————————————————————————————————————————
——Java的应用领域
J2SE 主要用来开发桌面应用软件。
J2ME 嵌入式开发,例如手机里的软件,掌上电脑软件等等。
J2EE 属于网络编程,例如JSP等等,做网站用。
——参考资料
张孝祥 《Java就业培训》
林信良 《Java学习笔记》
机械工业 《Java核心编程》
机械工业 《Java编程思想》
——配置环境变量
set classpath=.;自定义目录 //则先查找当前目录,然后再查找自定义目录,如果没有点,则只查找自定义目录
——标识符
在程序中自定义一些名称。
由26个英文字母大小写、数字0-9符号 _ $ 组成。
定义合法标识符规则:
1、数字不可以开头。
2、不可以使用关键字。
Java中严格区分大小写。
Java中名称的规范:
包名:
多单词组成时所有字母都小写。
类名接口名:
多单词组成时,所有单词的首字母大写。
变量名和函数名:
多单词组成时,第一个单词首字母小写,第二个单词开始每个单词首字母大写。
常量名:
所有字母都大写,多单次时每个单词用下划线连接。
——常量
常量表示不能改变的数值。
Java中常量的分类:
1、整数常量:所有整数。
2、小数常量:所有小数。
3、布尔型常量:true false
4、字符型常量:将一个数字、字母或者符号用单引号(‘ ‘)标识。
5、字符串常量:将一个或多个字符用双引号标识。
6、null常量:只有一个数值,就是null。
对于整数:
二进制:0 1
十进制:0-9,逢十进一。
八进制:0-7,逢八进一,用0开头表示。
十六进制:0-9,A-F,逢16进一,用0x开头表示。
——变量
变量的概念
内存中的一个存储区域。
该区域有自己的名称(变量名)和类型(数据类型)。
该区域的数据可以在同一类型范围内不停变化。
为什么要定义变量
用来存放同一类型的常量,并且可以重复使用。
变量使用注意:
变量的作用范围。(一对{ }内有效)
变量初始化。
定义变量的格式
数据类型 变量名 = 初始化值;
数据类型
基本数据类型:
数值型:
整数类型:
byte 1个字节 -128~127 -2^7~2^7-1
short 2个字节 -32768~32767 -2^15~2^15-1
int 4个字节 (默认)
long 8个字节 赋值时后面一般加小写L
浮点型:
float 4个字节 赋值时后面一般加f
double 8个字节(默认)
字符型:char 2个字节 //Java中字符型变量占两个字节。
布尔型:boolean
引用数据类型
类:class
接口:interface
数组:[ ]
整数默认 int 小数默认 double
——强制类型转换
由于Java是强类型语言,所以赋值时需要注意类型转换问题。
当一个java算术表达式中包含多个基本类型的值时,整个算术表达式的数据类型将发生自动提升。Java定义如下的自动提升规则:
1. 所有byte型、short型和char型将被提升到int型。
2. 整个算术表达式的数据类型自动提升到与表达式中最高等级操作数同样的类型。
float 和 int 进行运算时,以 float 为准。
——运算符
取模运算时,假如其中一数为负数,则只看运算符左边的数字:
-2%5 余数为-2。
6%-8 余数为6。
-10%-5 余数为0。
+ 号
字符串连接符:
“haha”+“heihei” 输出结果为 hahaheihei
字符串数据和任何数据使用+相连接,最后都会变成字符串。
System.out.println("haha"+ (++a)); 输出结果为:haha2 // ln表示括号内所有语句输出完毕后在结尾处换行一次
System.out.println("a = "+5); 输出结果为:a = 5
——转义字符
\n:换行 Linux中只有一个换行符:\n
\b:退格Backspace Windows中有两个换行符:\n \r
\r:回车 DOS中可以直接识别 \n
\t:下一个制表位
/*
System.out.print("hello \r world");
为什么输出结果是:
——————
world"
——————
在早期打字机中,每打完一行,会先跳回行首,再执行换行。
*/
如何输出上引号
System.out.println("\"hello world\"");
输出结果:”hello world“
char ch = ‘\n‘;也可以,因为\n转义完之后是一个换行符。
char ch = ‘啊‘;也可以,因为Java中char占2个字节,一个汉字也占两个字节。
——赋值运算符
shotr x = 4;
x = x + 5; //编译失败
而
short s = 3;
s += 5; //编译成功,因为x = x + 5;是两次运算,在运算过程中会进行自动类型提升,运算后的结果是int类型,无法赋给short类型
的变量;而 s += 5;只进行一次运算,在运算过程中会自动进行强制类型转换。
——比较运算符
比较运算符的运行结果都是boolean型,要么是true要么是false。
——逻辑运算符
& | ^ !
^:异或(相同为假,不同为真)
true ^ true = false;
true ^ false = true;
false ^ true = true;
false ^ false = false;
^:两边相同结果是 false
两边不同结果是 true
& 和 &&的区别:(一假全假)
&:无论左边是 true 还是 false ,都不会影响右边的运算。
&&:当左边为 false 时,右边不运算。
| 和 ||的区别:(一真全真)
|:两边都参与运算。
||:当左边为 true 时,右边不运算。(短路)
——按位操作符
与运算:& 或运算:| 异或运算:^ 取反:~
一假全假——
如果两个输入位都是1,则按位与(&)运算,生成一个输出位1;否则生成一个输出位0。
一真全真——
两个输入位只要有一个是1,则按位或( | )运算,生成一个输出位1;只有在两个输入位都是0的情况下,才会输出一个输出位0。
相同为假,不同为真——
如果输入位的某一个是1,但不全都是1,那么按位异或(^)运算,生成一个输出位1;两个都为0或1,生成一个输出位0。
&:(一假全假)
6 & 3 = 2;
6 ---- 110 0 就是假,1 就是真,一假全假。
3 ---- & 011
2 ---- 010 = 2
| :(一真全真)
或同理。
^ :(相同为假,不同为真)
异或同理,一个数异或同一个数两次,结果还是初始值,一般用于加密。
——位移算符(运算速度最快)
左移:<< 右移:>> 无符号右移:>>>
先把数字转换成二进制形式,然后将其左移或右移,缺少位用 0 补足。
| 3 << 2 = 12; | 3 << 1 = 6; | 3 << 3 = 24; |
| 3 * 4 = 12; | 3 * 2 = 6; | 3 * 8 = 24; |
| 3 * 2^2 = 12; | 3 * 2^1 = 6; | 3 * 2^3 = 24; |
往左移,移几位,就是十进制数字乘以二的几次幂。
往右移,移几位,就是十进制数字除以二的几次幂。(舍弃小数位)
右移(>>)时,正数高位补 0 ;负数高位补 1 。
无符号右移(>>>)时,高位补 0 。
练习:
1、最有效率的方式算出2乘以8等于几? 2 << 3
2、对两个整数变量的值进行互换(不需要第三方变量)。
(1)int n , m;
n = n + m; // 如果n和m 的值非常大,容易超出int范围。
m = n - m;
n = n - m;
(2)n = n ^ m;
m = n ^ m; // (n ^ m) ^ m n 异或 m 两次,结果还是 n。
n = n ^ m; // n ^ ( n ^ m) m 异或 n 两次,结果还是 m。
——流程控制
switch语句:
只接收四种类型的值:byte short int char /* JDK 7.0版本可以判断字符串 */
运行过程中如果没有 break ,则按顺序向下执行,直到 switch 结束。
break和continue:
在循环过程中可以通过break *; 或者是continue *; 来中断 / 跳出某个循环,例如:
/* 这是一个9*9乘法表,如果在标号为 w的for循环中有一个break w;
则运行到该语句时会中断 w 循环,break w; 也可以写在内循环当中。
标号只能用于循环。
*/
class chengfabiao
{
public static void main(String[] agrs)
{
w:for (int x = 1;x <= 9; x++)
{
q:for(int y = 1; y <= x; y++)
{
System.out.print(y+"*"+x+"="+y*x+"\t");
}
System.out.print("\n");
break w;
}
}
}
——函数重载
重载的定义:
在同一个类中,允许存在一个以上的同名函数,只要它们的参数个数或者参数类型不同即可。
重载的特点:
与返回值无关,只看参数列表。
重载的优点:
方便阅读,优化了程序设计。
重载示例:
1、public static int add(int x,int y)
{
return x+y;
}
2、public static int add(int x,int y,int z)
{
return x+y+z;
}
3、public static int add(int x,int y,int z)
{
return add(x,y)+z;
}
——数组
定义:
同一数据类型的集合。其实数组就是一个容器。(能存数据的地方就称之为容器)
在堆内存中定义数组时,都有默认值。
Java中栈内存,当函数调用结束时由系统自动释放,堆内存由Java垃圾回收机制不定时回收。
格式1:
元素类型 [ ] 数组名 = new 元素类型 [ 元素个数或数组长度 ]
示例:
int [ ] arr = new int [ 5 ];
int arr [ ] = new int [ 5 ];
格式2:
元素类型 [ ] 数组名 = new 元素类型 [ ] {元素1, 元素2, ......};
示例:
int [ ] arr = new int [ ] {3,5,7};
int [ ] arr = {3,5,7};
注意:当两个引用同时指向一个数组时,其中一个引用null,该数组不会变成垃圾值。(Java中的引用就是C语言的指针)
数组中有一个属性可以直接获取到数组元素的个数:length
字符串中获取长度的方法是:length();
使用方式:数组名称.length
数组的二分查找:
二分查找可以提高效率,但是必须保证数组中的元素是有序的。
右移一位相当于本数除以2。
/**
*
*
*2016年3月20日00:33:49
* 使用二分查找 获取数组元素
*
*@author WYC
*
*/
public class BinarySearchDemo {
publicstatic void main(String[] args)
{
intarr[] = {2,4,7,9,15,17,26,37,58,66};
System.out.println(binarySearch_2(arr,15));
}
//方法一
publicstatic int binarySearch_1(int arr[] , int key)
{
intmax = arr.length-1,min = 0 , mid = (max + min)>>1;
while(min<= max)
{
if(arr[mid]> key)
{
max= mid - 1;
}
elseif(arr[mid] < key)
{
min= mid + 1;
}
else
{
return mid;
}
mid= (max + min)>>1;
}
return-1;
}
//方法二
publicstatic int binarySearch_2(int arr[], int key)
{
intmin = 0, max = arr.length - 1, mid = (max + min) >> 1;
while(arr[mid]!= key)
{
if(arr[mid]> key)
{
max= mid - 1;
}
elseif(arr[mid] < key)
{
min= min + 1;
}
if(max< min)
{
return-1;
}
mid= (max + min) >> 1;
}
returnmid;
}
}
练习:
又一个有序的数组,想要将一个元素插入到该数组中,还要保证该数组是有序的。
如何获取该元素在数组中的位置。
将返回值改为 -min。
基于折半查找的返回要插入的脚标元素。
——面向对象(面向过程强调的是“动作”,面向对象强调的是“对象”)
对象就是多个功能的封装。
对象是通过new来产生的一个实体,存在于堆内存。
1、面向对象概念
理解面向对象:
面向对象是相对面向过程而言。
面向对象和面向过程都是一种思想。
面向过程:强调的是功能行为。
面向对象:将功能封装进对象,强调具备了功能的对象。
面向对象是基于面向过程的。
面向对象的特点:
面向对象是一种思想,它能让复杂的问题简单化,它能让我们的角色发生转变,从执行者转变为指挥者(怎么做的我不
知道,我只要结果)。
万物皆对象。
面向对象的三个特征:封装,继承,多态。
开发,其实就是找对象,使用对象,建对象,并维护对象之间的关系。
2、类与对象的关系
类就是对现实生活中事物的描述。
对象就是这类事物,实实在在存在的个体。
类的属性对应的是类中的变量,类的行为对应的是类中的函数(方法),其实定义类,就是在描述事物,属性和行为共同称为类
中的成员(成员变量和成员方法)。
通过类来创建一个对象:
类 abc = new 类();
abc是一个类类型变量(引用类型变量),类类型变量指向对象。
成员变量和局部变量作用范围:
成员变量作用于整个类中。
局部变量作用于函数中,或语句中。
成员变量有默认值,可以参与运算,局部变量无默认值。
* 其中数值类变量默认值为0
* 浮点型默认值为0.0
* 字符变量默认值为空格
* 布尔型变量默认值为false
成员变量和局部变量在内存中的存储:
成员变量:
成员变量随着类的加载而加载并且生成默认值。
在堆内存中,因为对象的存在,才在内存中存在。
局部变量:
存在于栈内存中。
匿名对象:
匿名对象是对象的简化形式。
new Person().age = 20;
匿名对象的两种使用情况:
当对对象方法仅进行一次调用的时候
匿名对象可以作为实际参数进行传递
匿名对象使用方式一:
当对对象的方法只调用一次时,可以用匿名对象来完成,这样写比较简单。
但是对一个对象进行多个成员调用,必须给这个对象起名字。
因为第二次匿名调用会使第一次匿名调用的对象成为垃圾值。
匿名对象使用方式二:
可以将匿名对象作为实际参数进行传递。
当类存在于不同文件之间,JVM会在当前目录下寻找class文件和java文件,没有则会报错。
3、封装(Encapsulation)
封装:是指隐藏对象的属性和细节实现,仅对外提供公共访问方式。
好处:将变化隔离
便于使用
提高重用性
提高安全性
封装原则:
将不需要对外提供的内容都隐藏起来。
把属性都隐藏,提供公共方法对其进行访问。
private:
私有,权限修饰符:用于修饰类中的成员(成员变量,成员函数)。
私有只在本类中有效。
将成员变量私有化以后,类以外即使建立了对象也不能直接访问,就需要在类中提供对应访问成员变量的方式。
一个属性通常对应两个方法:set 和 get。
注意:私有仅仅是封装的一种表现形式。
之所以对外提供访问方式,是因为可以在访问方式中加入逻辑判断等语句,对访问的数据进行操作,提高代码的健壮性。
4、构造函数
当创建对象时,因为调用了名字相同的类,所以通过函数重载对对象进行初始化。
特点:
1、函数名与类名相同
2、不用定义返回值类型
3、不可以写return语句
作用:
类对象一建立就会调用与之对应的构造函数。
构造函数的作用是给对象进行初始化。
注意:
1、默认构造方法的特点
给成员变量赋予初始值。
2、多个构造方法是以重载的形式存在的。
构造方法的小细节:
当一个类中没有定义构造方法时,那么系统会默认给该类加入一个空参数的构造函数(默认构造方法)。
当在类中自定义了构造函数以后,默认的构造函数就没有了。
构造函数和一般函数在写法上有不同,在运行方面也有不同:
构造函数是在对象一建立就运行,作用是给对象初始化;
而一般函数是对象调用才执行,是给对象添加对象具备的功能。
一个对象建立,构造函数只运行一次;
而一般方法可以被该对象调用多次。
什么时候定义构造函数?
当分析事物时,该事物存在即具备一些特性或者行为(例如人出生具备姓名年龄),那么将这些内容定义在构造函数中。
构造代码块:构造代码块中定义的是不同对象共性的初始化内容。
作用:
给对象进行初始化。
对象一建立就运行,而且优先于构造函数执行。
构造代码块和构造函数的区别:
构造代码块是给所有对象进行统一初始化,而构造函数是给对应的对象进行初始化。
格式:
{......}
区别于静态代码块( static{......} ),静态代码块在一个程序中只在类的第一次加载时执行一次。
构造代码块在每创建一个对象时调用一次。
5、this 关键字
this关键字是用于区分局部变量和成员变量同名的情况。
可以在构造函数中使用this关键字,在构造函数中,this代表所创建对象。
格式:
this.name
this代表它所在函数的所属对象的引用,简单来说,就是哪个对象在调用this所在的函数,this就代表哪个对象。
this的应用:
当定义类中功能时,该函数内部要用到调用该函数的对象,这时用this来表示该对象。
但凡本类功能内部使用到了本类对象,都用this表示。
构造函数间的函数调用:
this关键字可以用于构造函数中,并且构造函数间不能用 函数名(实参) 来调用,需要用this关键字来调用。
格式:
this(实参)。
注意:
this构造方法只能定义在构造函数的第一个语句,初始化动作要先执行。
要防止构造函数间无限调用,陷入死循环。
6、static 关键字
用于修饰成员(成员变量和成员函数)
被修饰后的成员具备以下特点:
随着类的加载而加载,并且随着类的消失而消失,说明它的生命周期最长,简称为类变量,未创建对象时可以用类名调用。
优先于对象存在(非静态成员变量只有创建对象时才会创建成员变量)
被所有对象所共享
可以直接被类名调用
类中静态成员互相调用时省略的是(类名 . ),而非静态成员变量互相调用时省略的是(this .)。
方法区,共享区,数据区,static静态变量存储在该区域内,所有对象可以共享该区域。
实例变量和类变量的区别:
1、存放位置
类变量随着类的加载而存在于方法区中
实例变量则随着对象的建立而存在于堆内存中
2、生命周期
类变量生命周期最长,随着类的消失而消失。
实例变量生命周期随着对象的消失而消失。
静态变量使用注意事项:
1、静态方法只能访问静态成员,成员包括方法和变量。
可以将对象作为参数传入静态方法,从而在静态方法中调用非静态方法。
非静态方法可以访问静态成员。
2、静态方法中不可以写 this、super关键字,无对象不能访问this。
3、主函数是静态的。
4、static静态变量的值可以修改,整个类享有同一个静态变量,一改全改。
静态有利有弊:
利:
对对象的共享数据进行单独空间的存储,节省空间,没有必要每一个对象中都存储一份。
可以直接被类名调用。
弊:
生命周期过长,浪费内存空间。
访问出现局限性。(静态只能访问静态)
6、main函数
public static void main(String[ ] agrs)
主函数:
是一个特殊的函数,作为程序的入口,可以被JVM调用
主函数的定义:
public:代表着该函数访问权限是最大的
static: 代表主函数随着类的加载就已经存在了
void: 主函数没有具体的返回值
main: 不是关键字,但是是一个特殊的单词,可以被JVM识别
(String[ ] args):函数的参数,参数类型是一个数组,该数组中的元素是字符串,是字符串类型的数组,args是变量名。
主函数是固定格式的:JVM识别。
注意:
当存在两个主函数时,参数列表不同,编译时不会报错,因为参数列表不同,相当于函数重载,不过入口还是(String[ ] arr)
JVM在调用主函数时,传入的是new String [0]; 推理如下:
class MainDemo
{
public static void main(String[] args)
{
System.out.println(agrs);
System.out.println(args.length);
}
}
在DOS命令行中运行 java MainDemo时,在MainDemo之后输入字符串,会将字符串作为函数参数传给主函数的String[ ]。
什么时候使用静态?
要从两方面下手:
因为静态修饰的内容有成员变量和函数。
什么时候定义静态变量(类变量)呢?
当对象中出现共享数据时,该数据被静态所修饰(一改全改)。
而对象中的特有数据要定义成非静态存在于堆内存中。
什么时候定义静态函数呢?
当功能内部没有访问到非静态数据(对象的特有数据),那么该功能可以定义成静态。
因为静态变量随着类的加载而加载,可以通过类名来调用静态变量,节省内存,提高效率。
静态的应用:
每一个应用程序中都有共性的功能,可以将这些功能进行抽取,独立封装成类,以便复用。
静态工具示例:
public class ArrayTool
{
public static void main(String[] args)
{
int [] arr = {1,4,2,6,7,3};
Tool tool = new Tool();
tool.show(arr);
tool.sort(arr);
tool.show(arr);
int max = tool.getMax(arr);
System.out.println("max = " + max);
int min = tool.getMin(arr);
System.out.println("min = " + min);
}
}
class Tool
{
public static int getMax(int [] arr)
{
int max = 0;
for(int i = 0; i < arr.length; i++)
{
if(arr[max] < arr[i])
{
arr[max] = arr[i];
}
}
return arr[max];
}
public static int getMin(int [] arr)
{
int min = 0;
for(int i = 0; i < arr.length; i++)
{
if(arr[min] > arr[i])
{
arr[min] = arr[i];
}
}
return arr[min];
}
public static void sort(int [] arr)
{
for(int i = 0;i < arr.length-1; i++)
{
for(int j = i+1; j< arr.length; j++)
{
if(arr[i] < arr[j])
{
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
}
}
public void show(int [] arr)
{
System.out.println("输出数组:");
for(int i = 0; i < arr.length; i++)
{
System.out.print(arr[i]);
}
System.out.println();
System.out.println("输出结束!");
}
}
虽然可以通过建立ArrayTool的对象使用这些工具方法,对数组进行操作,但是发现了问题:
1、对象是用于封装数据的,但是ArrayTool对象中并未封装特有数据。
2、操作数组的每一个方法都没有用到ArrayTool对象中的特有数据。
这时就考虑,让程序更严谨,是不需要对象的。
可以将ArrayTool中的方法都定义成static的,直接通过类名调用即可。
注意:
将方法都静态后,可以方便使用,但是该类还是可以被其他程序建立对象,为了使程序更加严谨:
强制让该类不能建立对象,可以通过将构造函数私有化来完成。
private ArrayTool ( ) { }
能私有的都私有。
7、帮助文档的制作 (也称为API文档,Application Program Interface 应用程序接口)
在DOS命令行下输入:javadoc -d myhelp -author -version ArrayTool.java
javadoc是Java自带工具,用于生成帮助文档。
-d 表示directory目录的意思
myhelp 表示创建文件夹的名字(如果需要,可以在-d之后加上自定义目录,如:c:\program\)
-author 表示生成文档的同时生成作者
-version 表示生成文档的同时生成文档版本
ArrayTool.java 表示源文件
注意:需要在源文件的class类前加上public,保证权限足够,才能够创建帮助文档。private权限下的无法生成帮助文档。
一个类中默认会有一个空参数的构造函数,该构造函数权限和所属类一致,如果类被public修饰,那么默认的构造函数也
带public修饰符,如果类没有被public修饰,那么默认的构造函数也没有public修饰。简单来说,默认构造函数的权限是
随着类的变化而变化的。如果将类名private,则无法生成构造函数帮助文档。
示例如下:
/**
这是一个可以对数组进行操作的工具类,该类中提供了获取最值、排序等功能。
@author 张三 //author表示作者,可以从源文件中提取,写入帮助文档
@version V1.0 version表示版本,可以从源文件中提取,写入帮助文档
*/
函数前写
/**
@param 函数参数 参数描述
@return 返回值描述
*/
8、静态代码块
随着类的加载而加载,只在类加载的同时执行一次,并且优先于主函数执行。
static
{
执行语句。
}
注意:
只有用到类中的内容时,静态代码块才会执行,而StaticCode s = null,静态代码块不会执行,只建立引用,类不会加载。
静态代码块中不能使用this,构造代码块中可以使用this。
9、静态代码块、构造代码块和构造函数的区别
静态代码块:用于给类初始化,类加载时就会被加载执行,只加载一次,优先于构造代码块。
构造代码块:用于给对象初始化,只要建立对象该部分就会被执行,且优先于构造函数。
构造函数: 给对应对象初始化的,建立对象时,选择相应的构造函数初始化对象。
创建对象时,三者被加载执行顺序:静态代码块--->构造代码块--->构造函数
10、对象的初始化过程
Person p = new Person();
1、在栈内存中,开辟main函数的空间,建立main函数的变量p。
2、加载类文件:因为new要用到Person.class文件,所以要先从硬盘中找到Person.class类文件,并加载到内存当中。
加载类文件时,除了非静态成员变量(对象的特有属性)不会被加载,其他的都会被加载。
3、执行类中的静态代码块:如果有静态代码块的话,对Person.class类进行初始化。
4、开辟空间:在堆内存中开辟空间,分配内存地址。
5、默认初始化:在堆内存中建立对象的特有属性,并进行默认初始化。
6、显示初始化:对属性进行显式初始化(编写人员通过语句进行的初始化叫做显式初始化,比如自定义构造函数;而系统
默认的构造函数就是隐式初始化)
7、构造代码块:执行类中的构造代码块,对对象进行构造代码块初始化。
8、构造函数初始化:对对象进行对应的构造函数初始化。
9、将内存地址赋给栈内存中的变量p。
p.setName("lisi");
1、在栈内存中开辟setName方法的空间,里面有:对象的引用 this,临时变量name。
2、将p的地址赋给this,this就指向了堆中调用该方法的对象。
3、在传参时将"lisi"赋值给临时变量name。
4、将临时变量的值赋给this.name。
注意:要想使用类中的成员,必须通过类名或者this或者super调用。
11、单例设计模式
设计模式:解决某一类问题最行之有效的方法,Java中有23种设计模式:
单例设计模式:解决一个类在内存中只存在一个对象。
想要保证对象唯一:
1、为了避免其他程序过多建立该类对象,先禁止其他程序建立该类对象。
2、为了让其他程序可以访问到该类对象,可以在本类中自定义一个对象。
3、为了方便其他程序对自定义对象的访问,可以对外提供一些访问方式。
以上三步代码体现方式:
1、将构造函数私有化(对应1)
2、在类中创建一个本类对象(对应2)
3、提供一个方法可以获取到该对象(对应3)
//饿汉式,这个是先初始化对象
class Single1
{
private Single(){};
private static Single s = new Single();
public static Single getInstance(); //如果想要调用成员变量或者成员函数,只能通过类名或者对象来实现
{ 因为没有对象,所以只能通过类名来调用,并且只能是static
return s;
}
}
//懒汉式,对象是方法被调用时才初始化,也叫做对象的延时加载。
class Single2
{
private Single(){ };
private static Single s = null;
public static getInstance()
{
if (null == s)
s = new Single();
return s;
}
}
class SingleDemo
{
public static void main(String[] args)
{
Single s1 = Single.getInstance(); //因为类变量是静态的,所以只能创建一个,无法重复创建,但是可以修改值
Single s2 = Single.getInstance();
}
}
懒汉式和饿汉式的区别:
饿汉式
Single类一进内存,就已经创建好了对象。定义单例,建议使用饿汉式。
懒汉式
Single类进内存,对象还没有存在,只有调用了getInstance方法时,才建立对象。
懒汉式在运行中容易出现差错,因为CPU来回切换运行程序时容易创建多个对象,为了避免此情况,可以加
入synchronized关键字,保证对象唯一。
/*
饿汉式-多线程
*/
class Single3
{
private Single(){};
private static Single s = null;
public static getInstance()
{
if(null == s)
{
synchronized(Single.class) //可以避免每次都判断是否上锁,提高程序运行效率
{
if(null == s)
{
s = new Single();
}
}
}
}
}
对于事物该怎么描述,还怎么描述,当需要将该事物的对象保证在内存中唯一时,就将以上的三步加上即可。
12、类的生命周期
在类使用完之后,如果满足下面的情况,类就会被卸载:
1、该类所有的实例(对象)都已经被回收,也就是Java堆中不存在该类的任何实例。
2、加载该类的ClassLoader(类加载器)已经被回收。
3、该类对应的java.langClass对象没有任何地方被引用,无法再任何地方通过反射访问该类的方法。
如果以上三个条件全部满足,JVM就会在方法区垃圾回收的时候对类进行卸载,类的卸载过程其实就是在方法区中清空类信息
,Java类的整个生命周期就结束了。
——继承
一个新类从已有的类那里获得其已有的属性和方法,这种现象叫类的继承。
这个新类被称为子类,也叫派生类,已有的那个类叫做父类,也叫做基类。
继承的好处:
代码得到极大的重用。
形成一种类的层次体系结构 。
为多态创造条件。
继承的实现方式:
class SubClass extends SuperClass // SuperClass是父类的意思
{
··········
}
利用继承可以较好地模拟出现实世界事物之间的联系
注意:Java只支持单继承,不支持多继承,多继承容易带来安全隐患(当父类中定义了相同功能时,创建子类对象时,无法确定执行
哪个代码),C++中支持多继承,但是Java中保留了这种机制,并用另一种体现形式来完成表示,叫做多实现。
Java支持多层继承:C继承B,B再继承A。
Java中也可以继承构造代码块和静态代码块,但是不能继承构造方法。
子类可以继承父类所有的成员变量和成员方法,但子类永远无法继承父类的构造方法。在子类的构造方法中可以使用语
句super(参数列表)来调用父类的构造方法。
当子类父类类成员名相同时,可以使用super来调用父类成员,否则默认使用this调用子类成员。private情况下无法访问父类成员。
1、子父类中变量的特点
如果子父类中出现非私有的同名成员变量时,子类要访问本类中的变量需要this关键字,访问父类中的同名变量需要super关键字。
2、子父类中的函数(重写,覆盖)
当子类出现和父类一模一样的函数时,子类对象调用该函数,会运行子类函数的内容,如同父类函数被覆盖一样,这种情况是函数
的另一个特性:重写(覆盖)。
当子类继承父类,并将父类的功能集成到子类中,但是子类虽具备该功能,而功能的内容却和父类不一致,这时,没有必要
定义新功能,而是使用覆盖特性,保留父类的功能定义,并重写该功能。
覆盖:
1、子类覆盖父类,必须保证子类权限大于等于父类权限,才可以覆盖。
2、静态只能覆盖(复写)静态,不能覆盖非静态(静态先加载)。
重写和重载的区别:
重载:只看同名函数的参数列表
重写:子父类方法要一模一样,包括返回值类型。
3、子父类中的构造函数
在对子类对象进行初始化时,父类的构造函数也会运行,那是因为子类的构造函数默认第一行有一条隐式的语句:super();
super();语句会访问父类中空参数的构造函数,而且子类中所有的构造函数默认的第一行都是super();
为什么子类一定要访问父类中的构造函数?
因为父类中的数据子类可以直接获取,所以子类对象在建立时需要先查看父类是如何对这些数据进行初始化的,所以子类在
对象初始化时,要先访问父类中的构造函数。如果要访问父类中指定的构造函数,可以通过手动定义super来指定。
如果父类中有private的成员,虽然子类不能访问,但是子类一定会继承过来并开辟物理内存来存储父类的private成员,所以会
默认调用一下父类的无参构造方法super();来访问父类中的私有成员,以便开辟内存。
注意:super语句一定定义在子类构造函数的第一行。
子类当中至少有一个构造方法去访问父类构造方法。
结论:子类的所有的构造函数,默认都会访问父类中空参数的构造函数,因为子类每一个构造函数内的第一行都有一句super();
当父类中没有空参数的构造函数时,子类必须手动通过super语句的形式来指定要访问的构造函数。
当然,子类的构造函数第一行也可以手动指定this语句来访问本类中的构造函数,子类中至少有一个构造函数会访问父类
中的构造函数。
私有不能被继承
私有物理存储上已经被继承,不过逻辑上程序员不能去访问它
因此继承必须慎重,否则会浪费内存
——继承权限问题(重点)
子类的所有方法内部都可以访问父类除私有成员以外的所有成员,所谓子类的所有方法也包括子类的私有方法。
通过子类对象名可以访问:
1、父类除私有成员外的所有成员。
2、子类本身的除私有成员外的所有成员。
例如:SupClass.Sub();
子类可以继承父类除私有成员外的所有成员。
父类的私有成员不可以被子类继承,其他的成员都可以被子类继承。
——类的继承举例:
/*
2015年6月2日21:52:21
本程序证明了:
1、子类内部可以访问父类非私有的成员
私有成员无法被子类方法访问
2、通过子类对象名只能访问从父类继承过来的非私有成员
总结:
*/
class A
{
public int i;
protected int j;
private int k;
public void g(){}
private void s(){}
protected void b(){}
}
class B extends A
{
private void f()
{
i = 10;
j = 20;
//k = 30;//error 私有属性不能被继承
g();
b();
//s();//error 私有的方法不能被继承
}
public void f()
{
i = 10;
j = 20;
//k = 30;//error 私有属性不能被继承
g();
b();
//s();//error 私有的方法不能被继承
}
}
class M
{
public static void main(String[] args)
{
B bb = new B();
bb.i = 20;
bb.j = 30;
bb.b();
bb.g();
//bb.s();通过子类对象名只能访问从父类继承过来的非私有成员
}
}
——不同访问修饰符
——final 关键字
1、final可以修饰类,方法,变量。
2、final修饰的类不可以被继承。用final可以避免被继承,避免被子类复写函数。
3、final修饰的方法不可以被覆盖(复写)。
4、final修饰的变量只能被赋值一次。既可以修饰成员变量,也可以修饰局部变量。
当描述事物时,一些数据只是固定的,比如π,那么这时为了增强阅读性,都给这些值起个名字,方便阅读,而这个值不需要改变
,所以加上final修饰,作为常量:常量的书写规范所有字母都大写,如果有多个单词组成,单词间通过下划线连接。
5、内部类只能访问被final修饰的局部变量。
——抽象类:(只能修饰类方法)
当多个类中出现相同功能,但是功能主体不同,这时可以进行向上抽取,但是只抽取功能定义,而不抽取功能主体。
抽象方法必须存放在抽象类中。
抽象类的特点:
1、抽象方法一定定义在抽象类中。
2、抽象方法和抽象类都必须被abstract关键字修饰。
3、抽象类不可以用new创建对象,因为调用抽象方法没意义。
4、抽象类中的方法要被调用,必须由子类复写其所有的抽象方法后,再建立子类对象调用
如果子类对象只覆盖了部分抽象方法,那么该子类还是一个抽象类。
5、抽象类中可以有抽象方法,也可以有非抽象方法。
6、构造方法和静态方法不能抽象。
抽象类和一般类没有太大的不同,该如何描述事物就如何描述事物,只不过该事物内部有一些看不懂的东西,这些不确定的部分,也
是该事物的功能,要明确定义函数,但是无法定义函数主体,则需要子类对象去按需定义。
抽象类比一般函数多了个抽象函数,就是在类中可以定义抽象方法。
抽象类不可以实例化,必须通过子类来实例化。
特殊:
抽象类中可以不定义抽象方法,这样做仅仅是不让该类创建对象。
abstract class Employee
{
public String name;
public String id;
public double pay;
public abstract void work();
Employee(String name,String id,double pay)
{
this.name = name;
this.id = id;
this.pay = pay;
}
}
class Manager extends Employee
{
double bonus;
Manager(String name,String id, double pay)
{
super(name,id,pay);
this.bonus = bonus;
}
public void work()
{
System.out.println("Manager.name = "+this.name + "\nManager.id = " + this.id + "\nManager.pay = "+ this.pay + "\nManager.bonus = " +this.bonus);
}
}
class Profession extends Employee
{
Profession(String name,String id,double pay)
{
super(name,id,pay);
}
public void work()
{
System.out.println("Profession");
}
}
class AbstractTest
{
public static void main(String[] args)
{
Manager m = new Manager("zhangsan","研发部01",9800);
m.bonus = 5000f;
m.work();
}
}
——接口
接口和抽象类的父类引用可以接收子类对象。
接口的目的:
避免单继承的局限性:
可以解决类的单一继承问题。
接口之间可以实现多继承。
接口之间可以多实现。
解耦合
电脑上设置USB接口。
可以做到随时更换。
接口回调
在团队开发时,先写好接口。
其他类可以调用接口的方法,哪怕还没有完全实现。
指定标准和规范
具有指定的属性值
必须实现指定的方法。
格式:
interface aa{ }
接口中成员修饰符是固定的(接口中的成员都是public)
成员常量:
public static final int x = 3;
成员函数:
public abstract void show();
接口的出现将“多继承”通过另一种形式体现出来,即多实现。
注意:子类要将接口中的所有成员全都实例化才能实现。
接口方法不能有主体。
接口是不可以创建对象的,因为接口中包含抽象方法,需要被子类实现,子类对接口中的抽象方法全都覆盖后,才可以实例化,否则
子类还是一个抽象类。
接口可以被类多实现:
一个类可以同时实现多个接口,一个接口可以被类多实现,也是对多继承不支持的转换形式,Java支持多实现。
一个类在继承一个类的同时还能实现多个接口。
Java支持多继承,只有接口和接口之间支持多继承。
——接口的特点
接口中的所有方法都是抽象的、public的,但是可以不加修饰符,因为只要是interface,就默认强制abstract、public。
接口中的所有属性都是public static final,可以省略不写。
1、接口是对外暴露的规则。
可以通过接口访问类。
2、接口是程序的功能扩展。
将功能单独封装,在类中implements接口。
3、接口可以用来多实现。
降低类之间的耦合性。
4、类与接口之间是实现关系,而且类可以继承一个类的同时实现多个接口。
5、接口与接口之间可以有继承关系。
6、接口中可以定义静态属性,但是不能定义静态方法。
——多态
多态自始至终都是子类对象在做变化。
定义:
可以理解为事物存在的多种体现形态。
某一类事物的多种存在形式,例如猫是猫科动物,同时猫也可以成为动物。
1、多态的体现
父类的引用指向了自己的子类对象。(父类的引用也可以接收自己的子类对象)
2、多态的前提
必须是类与类之间有关系,要么是继承要么是实现。
通常还有一个前提,就是覆盖
3、多态的好处
多态的出现大大的提高了程序的扩展性。
后期再出现对象,方法不需要修改,可以直接执行。
4、多态的弊端
提高了扩展性,但是只能使用父类的引用访问父类中的成员。
5、多态的应用
6、多态的出现代码中的特点(多态使用的注意事项)
在多态中(当父类指向子类对象时)成员函数(非静态,可重写)的特点:
在编译时期:参阅引用所属的类中是否有调用的方法,如果有则编译通过,如果没有编译失败。
在运行时期:参阅对象所属类中是否有调用的方法。
Fu f = new Zi();
编译时查看Fu中有无调用的方法,运行时则查看new Zi()中有无调用的方法。
如果子类对象有该方法,就运行子类对象的,如果没有就运行父类的。 (其实就是子类的,因为子类已经继承了父类)。
简单总结就是:
成员函数在多态调用时,编译看左边,运行看右边。
在多态中成员变量的特点:
无论编译和运行,都参考左边(引用型变量所属的类)。
在多态中,静态成员函数的特点:
无论编译和运行,都参考左边。
多态调用情况:
当调用成员变量时,默认调用父类变量。
当调用成员方法时,默认调用子类方法。
7、类型转换
Animal a = new Cat(); //类型提升,向上转型。
此时a引用中只有Animal中的成员方法,并无Cat中的成员方法,如果想要使用父类的引用调用Cat中的成员方法,需要类型转换,向下转型:
Cat c = (Cat)a; //类型转换。
此时将Animal的引用a转换为Cat类型的引用c,c中既包含Animal的方法也包含子类Cat中的特有方法。
千万不要将父类对象转换成子类类型。Cat c = (Cat)a;转换的其实是父类的引用。
我们能转换的是父类的引用,当父类引用指向了自己的子类对象时,该引用可以被提升,也可以被强制转换。
多态自始至终都是子类对象在变化。
多态示例程序:
(1)
abstract class Animal
{
public abstract void eat();
}
class Dog extends Animal
{
public void eat()
{
System.out.println("dog eatting~~~~~~~");
}
}
class Cat extends Animal
{
public void eat()
{
System.out.println("cat eatting~~~~~~~");
}
public void act()
{
System.out.println("爬树");
}
}
class Pig extends Animal
{
public void eat()
{
System.out.println("pig eatting~~~~~~");
}
}
public class DuotaiDemo {
public static void main(String[] args) {
Dog d = new Dog();
d.eat();
Cat c = new Cat();
c.eat();
function(new Dog());
function(new Cat());
function(new Pig());
Animal a = new Cat();//类型提升,向上转型,转成父类型。
a.eat();
//如果想要调用猫的特有方法时,如何操作?
//强制将父类的引用,转成子类类型,向下转型。千万不要将父类对象转成子类类型。
//当父类引用指向了自己的子类对象时,该引用可以被提升,也可以被转换。
//多态自始至终都是子类对象在变化。
Cat cat = (Cat)a;
cat.act();
}
public static void function(Dog d){
d.eat();
}
public static void function(Cat c){
c.eat();
}
public static void function(Animal a){
a.eat();
}
}
(2)
abstract class Student
{
public abstract void study();
public void sleep()
{
System.out.println("Sleeping~~~~~~~");
}
}
class Daxue extends Student
{
public void study()
{
System.out.println("大学课程");
}
}
class Gaozhong extends Student
{
public void study()
{
System.out.println("高中课程");
}
public void sleep()
{
System.out.println("晚睡早起");
}
}
public class DuotaiDemo2 {
public static void main(String[] args) {
Operate o = new Operate();
o.student(new Daxue());
o.student(new Gaozhong());
}
}
class Operate
{
public void student(Student st)
{
st.study();
st.sleep();
}
}
(3)多态的主板示例
interface PCI
{
public void open();
public void close();
}
class mainBoard
{
public void run()
{
System.out.println("mainboard running!");
}
public void usePCI(PCI p)
{
p.open();
p.close();
}
}
class netCard implements PCI
{
public void open()
{
System.out.println("netcard running!");
}
public void close()
{
System.out.println("netcard closed!");
}
}
class soundCard implements PCI
{
public void open()
{
System.out.println("soundcard running!");
}
public void close()
{
System.out.println("soundcard closed!");
}
}
public class DuotaiDemo4 {
public static void main(String[] args)
{
mainBoard m = new mainBoard();
m.run();
m.usePCI(new netCard());
m.usePCI(new soundCard());
}
}
——Object类__equals()
1、Object类是所有对象的直接或者间接父类,该类中定义的肯定是所有对象都具备的功能。
2、equals()比较的是堆中两个对象的内容,而“==”比较的是栈中两个对象的引用(内存空间)。
class Demo
{
int num;
Demo(int num)
{
this.num = num;
}
public boolean equals(Object obj)
{
if(!(obj instanceof Demo)) //如果用到对象中特有数据,就需要判断和转换的动作。
return false;
Demo d = (Demo)obj;
return this.num == d.num;
}
}
public class equals {
public static void main(String[] args)
{
Demo d1 = new Demo(2);
Demo d2 = new Demo(2);
System.out.println(d1.equals(d2));
}
}
3、toString方法返回的是任意对象的所属类和哈希值。
Student st = new Student();
System.out.println("st.toString() = " + st.toString());
输出
st.toString() = it.wang.Student@659e0bfd
4、类自带的equals()和toString()方法大多无意义,一般会通过复写方法自定义方法内容。
——内部类
1、将一个类定义在另一个类的里面,一个类里面那个类就称为内部类(内置类,嵌套类)。
catch(Exception ex) //Exception ex = new ArithmeticException();实际上就是多态,父类引用调用子类对象,所以ex可以使用父
类中的方法
public int div(int a,int b) throws ArithmeticException,ArrayIndexOutOfBoundsException
throw new ZidingyiException("被除数不能为负数",b);//手动通过throw关键字抛出一个自定义异常。