Java基础-面向对象
Java编程-面向对象入门(续)
1、面向对象(static关键字)
static(静态)关键字:用于修饰成员(成员变量和成员函数) 被修饰后的成员具备以下特点:
(1)、随着类的加载而加载。
(2)、优先于对象存在。
(3)、被所有对象所共存。
(4)、可以直接被类名所调用。
使用注意:
(1)、静态方法只能访问静态成员。
(2)、静态方法中不可以写this,super关键字。
(3)、主函数是静态的。
Eg:
public class Demo1
{
public static void main(String []args)
{
Person p1 = new Person();
Person p2 = new Person();
p1.name = "张三";
p2.name = "李四";
p1.show();
p2.show();
}
}
class Person
{
String name ; //成员变量,实例变量
String country = "CN";
public void show()
{
System.out.println(name+"::"+country);
}
}
输出结果:
张三::CN
李四::CN
从以上代码可以发现,无论新建多少个新的Person类的对象,对象的国籍都是CN,我们可以考虑使用static关键字,让country给对象所共享,所以我们可以在代码String country = "CN";前加上static关键字。
static关键字用法:是一个修饰符,用于修饰成员(成员变量,成员函数)。当成员被静态修饰后,就多了一种调用方式,除了可以被对象调用外,还可以直接被类名调用:类名.成员变量。例如Person.country;,注意,已经被static修饰过的成员变量已经不在堆内存中了,而存在于一个叫做方法区的地方。如图:
static特点:
(1)、随着类的加载而加载。(也就是说静态会随着类的消失而消失,所以生命周期最长,消耗内存)
(2)、被所有对象所共享。
(3)、优先于对象存在。(明确一点,静态是先存在的,对象是后存在的。)
(4)、可以直接被类名调用。
-
实例变量和类变量的区别:
(1)、存放位置:类变量随着类的加载而存在于方法区中,实例变量随着对象的建立而存在于堆内存中。 (2)、生命周期:类变量生命周期最长,随着类的消失而消失。实例变量的生命周期随着对象的消失而消失。
-
静态使用的注意事项:
(1)、静态方法只能访问静态成员,非静态方法既可以访问静态也可以访问非静态。 (2)、静态方法中不可以定义this,super关键字,因为静态优先对象存在,所以静态方法中不可以出现this。 (3)、主函数是静态的。
-
静态有利有弊:
(1)、利:对对象的共享数据进行单独空间的存储,节省空间,没必要每一个对象中都存储一份。 (2)、生命周期过长,访问出现局限性。(静态虽好,只能访问静态。)
2、面向对象(main函数)
(1)、主函数是静态的:public static void main(String [] args)
args : 是arguments的缩写,是参数的意思,它是一个变量名,可以改变,这样写是一种习惯而已。
主函数:是一个特殊的函数,作为一个程序的入口,可以被JVM调用。
主函数的定义:
public :代表着该函数访问权是最大的。
static:代表主函数随着类的加载就已经存在了。
void:主函数没有具体的返回值。
main:不是关键字,但是是一个特殊的单词,可以被JVM识别。
(String [] args):函数的参数,参数类型是一个数组,该数组中的元素是字符串,字符串类型的数组。
主函数是固定格式的:JVM识别。
Eg:
public class Demo1
{
public static void main(String []args)
{
System.out.println(args);
}
}
输出结果:
[Ljava.lang.String;@1db9742
由此可见,JVM在调用主函数时,传入的是new String[0];
3、面向对象(静态什么时候使用)
- 什么时候使用静态呢?
要从两方面下手(因为静态修饰的内容有成员变量和函数):
(1)、什么时候定义静态变量(类变量)呢?
当对象中出现共享数据时,该数据被静态所修饰,对象中的特有数据要定义成非静态存在于堆内存中。
(2)、什么时候定义静态函数呢?
当功能内部没有访问到非静态数据(对象的特有数据),那么该功能可以定义成静态的。
Eg:
public class Demo1
{
public static void main(String []args)
{
Person p = new Person();
p.show(); //新建对象调用show方法
Person.show(); //由于Person类中的show方法已经定义成了静态,所以我们可以类名直接调用。
}
}
class Person
{
String name ;
public static void show ()
{
System.out.println("haha");
}
}
输出结果:
haha
haha
4、面向对象(静态的应用--工具类)
/*静态的应用*/
Eg:
public class Demo1
{
public static void main(String []args)
{
int [] arr = {3,4,1,8}; //自定义数组
int max = Test.getMax(arr); //类名Test直接调用已经用static定义的工具函数getMax
System.out.println("Max= "+max); //输出最大数
}
}
class Test //测试工具类
{
public static int getMax(int []arr) //用静态来定义好的工具函数getMax
{
int max = 0;
for(int x = 1;x<arr.length;x++)
{
if(arr[x]>arr[max])
{
max = x;
}
}
return arr[max];
}
}
静态的应用: 每一个应用程序中都有共性功能,可以将这些功能进行抽取,独立封装,以便复用。
/*静态的应用2*/
Eg:
public class Demo1
{
public static void main(String []args)
{
int [] arr = {3,1,87,32,8}; //自定义一个整数数组
int max = Tool.getMax(arr); //用工具类直接调用方法,得到的数组中最大数存入整数max中
int min = Tool.getMin(arr); //用工具类直接调用方法,得到的数组中最小数存入整数min中
Tool.selectSort(arr); 用工具类直接调用方法
System.out.println(max);
System.out.println(min);
for(int x = 0 ; x< arr.length;x++)
{
System.out.print(arr[x]+" ");
}
}
}
class Tool //工具类
{
private Tool(){}; //私有化构造函数,为了不让用户建立该工具类的对象。
public static int getMax(int []arr) //用静态来定义好的工具函数getMax,获取数组中最大数
{
int max = 0;
for(int x = 1;x<arr.length;x++)
{
if(arr[x]>arr[max])
{
max = x;
}
}
return arr[max];
}
public static int getMin(int[]arr) //用静态来定义好的工具函数getMin,获取数组中最小数
{
int min = 0;
for(int x = 1;x<arr.length;x++)
{
if(arr[x]<arr[min])
{
min = x;
}
}
return arr[min];
}
public static void selectSort(int []arr) //用静态来定义好的工具函数selectSort,给数组从小到大排序
{
for(int x = 0 ; x<arr.length-1 ; x++)
{
for(int y = x+1;y<arr.length;y++)
{
if(arr[x]>arr[y])
{
swap(arr,x,y);
}
}
}
}
public static void bubbleSort(int []arr)//用静态来定义好的工具函数bubbleSort,用冒泡排序法给数组排序
{
for(int x= 0 ; x<arr.length-1;x++)
{
for(int y = 0 ; y<arr.length-x-1;y++)
{
if(arr[y]>arr[y+1])
{
swap(arr,y,y+1);
}
}
}
}
private static void swap(int[]arr,int a ,int b)//用静态且私有来定义好的工具函数swap,交换数组中的两个数
{
int temp = arr[a];
arr[a] = arr[b];
arr[b] = temp;
}
}
程序结果:
87
1
1 3 8 32 87
以上程序在主函数中都是用类名直接调用工具类中的方法,因为工具类中的方法都是用static来定义的,即在对象建立之前就存在,可以直接类名调用,例如Tool.selectSort(arr);,将方法都静态后,可以方便使用,但是该类还是可以被其他程序建立对象的,为了更为严谨,强制让该类不能建立对象,可以通过将构造函数私有化完成,即private Tool(){};
5、面向对象(帮助文档的制作 javadoc.exe)
如果某人共享了一个Tool.class文件在C:/myclass文件夹下,那么我们设置set classpath = .;c:/myclass,接下来,将Tool.class文件发送给其他人只要将该文件设置到classpath路径下,就可以使用该工具类,但是,很遗憾,该类中到底定义了多少个方法,对方却不清楚,因为该类并没有使用说明书。制作java的程序说明书,是通过文档注释来完成的。用上面的程序例子为例,文档注释的方法如下:
Eg:
public class Demo1
{
public static void main(String []args)
{
int [] arr = {3,1,87,32,8}; //自定义一个整数数组
int max = Tool.getMax(arr); //用工具类直接调用方法,得到的数组中最大数存入整数max中
int min = Tool.getMin(arr); //用工具类直接调用方法,得到的数组中最小数存入整数min中
Tool.selectSort(arr); 用工具类直接调用方法
System.out.println(max);
System.out.println(min);
for(int x = 0 ; x< arr.length;x++)
{
System.out.print(arr[x]+" ");
}
}
}
class Tool //工具类
{
/**
*这是一个可以对数组进行操作的工具类,该类中提供了获取数组中最值、排序等功能。
*@author 张三
*@version v1.1
* */
/**
* 获取一个整数数组中的最大值
* @param arr 接收一个int类型的数组
* @return 会返回一个该数组中的最大值
* */
public static int getMax(int []arr) //用静态来定义好的工具函数getMax,获取数组中最大数
{
int max = 0;
for(int x = 1;x<arr.length;x++)
{
if(arr[x]>arr[max])
{
max = x;
}
}
return arr[max];
}
/**
* 获取一个整数数组中的最小值
* @param arr 接收一个int类型的数组
* @return 会返回一个该数组中的最小值
* */
public static int getMin(int[]arr) //用静态来定义好的工具函数getMin,获取数组中最小数
{
int min = 0;
for(int x = 1;x<arr.length;x++)
{
if(arr[x]<arr[min])
{
min = x;
}
}
return arr[min];
}
/**
* 给整数数组进行排序
* @param arr 接收一个int类型的数组
* */
public static void selectSort(int []arr) //用静态来定义好的工具函数selectSort,给数组从小到大排序
{
for(int x = 0 ; x<arr.length-1 ; x++)
{
for(int y = x+1;y<arr.length;y++)
{
if(arr[x]>arr[y])
{
swap(arr,x,y);
}
}
}
}
/**
* 给整数数组进行冒泡排序
* @param arr 接收一个int类型的数组
* */
public static void bubbleSort(int []arr)//用静态来定义好的工具函数bubbleSort,用冒泡排序法给数组排序
{
for(int x= 0 ; x<arr.length-1;x++)
{
for(int y = 0 ; y<arr.length-x-1;y++)
{
if(arr[y]>arr[y+1])
{
swap(arr,y,y+1);
}
}
}
}
/**
* 交换数组中的两个数
* @param arr 接收一个int类型的数组
* @param a 接收数组中的角标值
* @param b 接收数组中的角标值
* */
private static void swap(int[]arr,int a ,int b)//用静态且私有来定义好的工具函数swap,交换数组中的两个数
{
int temp = arr[a];
arr[a] = arr[b];
arr[b] = temp;
}
}
然而如何对以上的代码生成程序说明书呢?这需要我们在命令行里输入javadoc -d myhelp -author -version Demo1.java,注意程序里的类必须是public的。如图:
输入命令并生成完毕后,我们就去相应的地址去找程序说明书,如图:
在myhelp文档里这么多的文件,我们只需要点击index这一文件即可,它是一个索引文件,也就是所谓的程序说明书,打开后如图:
- 一个类中默认会有一个空参数的构造函数,这个默认的构造函数的权限和所属类一致,如果类被public修饰,那么默认的构造函数也带public修饰符,如果类没有被public修饰,那么默认的构造函数,也没有public修饰,默认构造函数的权限是随着类的变化而变化的。
6、面向对象(静态代码块)
静态代码块格式:
static
{
静态代码块中的执行语句;
}
特点:随着类的加载而执行,只执行一次,并优先于主函数,用于给类进行初始化。
Eg:
class StaticCodeDemo
{
static //静态代码块,输出B
{
System.out.println("B");
}
public static void main(String []args)
{
new StaticCode();
new StaticCode(); //这个对象建立时不执行StaticCode类中的静态代码块了,只执行一次!
System.out.println("OVER");
}
static //静态代码块,输出C
{
System.out.println("C");
}
}
class StaticCode
{
static //静态代码块,输出A
{
System.out.println("A");
}
}
程序结果:
B
C
A
OVER
这是因为程序首先执行有主函数的类,所以虚拟机首先加载StaticCodeDemo类,因而首先执行第一个静态代码块,输出B,然后虚拟机读到new StaticCode();时,即新建一个StaticCode类的对象,因此StaticCode类加载,同时也执行类里的静态代码块,输出C,当虚拟机再读到new StaticCode();时,类里的静态代码块不再执行,最后读到最后一个静态代码块时输出A,最后输出OVER。
Eg2:
class StaticCodeDemo
{
public static void main(String[]args)
{
StaticCode.show(); //用静态调用类方法
}
}
class StaticCode
{
static //静态代码块,输出A
{
System.out.println("A");
}
public static void show()
{
System.out.println("show run");
}
}
结果输出:
A
show run
上例中是用静态调用类方法,在调用的同时也加载了类,因而执行了静态代码块。
Eg3:
class StaticCode
{
int num = 9;
StaticCode() //构造函数
{
System.out.println("B"); //没执行
}
static //静态代码块
{
System.out.println("A");
}
{ //构造代码块
System.out.println("C"+this.num);
}
StaticCode(int x) //构造函数重载
{
System.out.println("D");
}
public static void show() //静态方法
{
System.out.println("show run");
}
}
class StaticCodeDemo
{
public static void main(String[]args)
{
new StaticCode(4); //调用StaticCode类的构造函数。
}
}
程序结果:
A
C9
D
从主函数中可以看出,new StaticCode(4);直接调用了StaticCode类中的构造函数,然而先读静态代码块,输出A,然后读构造代码块的内容,输出C9,最后是读取构造函数,输出D。
7、面向对象(对象初始化的过程)
Eg:
class Person
{
private String name;
private int age ;
private static String country = "CN"; //静态定义字符串变量
{
System.out.println(name+".."+age); //构造代码块,输出变量name和age的值
}
Person(String name, int age) //构造函数重载,给name和age初始化
{
this.name = name ;
this.age = age ;
}
public void setName(String name) //类方法setName,给name赋值
{
this.name = name ;
}
public void speak() //类方法,输出当前name和age的值
{
System.out.println(this.name+"..."+this.age);
}
public static void showCountry() //类方法showCountry,显示当前对象的国籍
{
System.out.println("country = "+country);
}
}
class PersonDemo
{
public static void main(String[]args)
{
Person p = new Person("ZhangSan",20); //新建对象p并赋值
p.speak(); //调用类方法
}
}
输出结果:
null..0
ZhangSan...20
以上程序在建立新对象并赋值的同时,先执行Person类里的构造代码块,所以首先输出对象的name和age的默认值null..0,然后在进行初始化,调用speak()方法输出赋值后的name和age值ZhangSan...20。 详细来说明以上代码的运作流程:Person p = new Person("ZhangSan",20); 该句话做了什么事情:
(1)、因为new用到了Person.class,所以会先找到Person.class文件并加载到内存。
(2)、执行该类中的static代码块,如果有的话,给Person.class类进行初始化。
(3)、在堆内存中开辟空间,分配内存地址。
(4)、在堆内存中建立对象的特有属性,并进行默认初始化。
(5)、对属性进行显示初始化。
(6)、对对象进行构造代码块初始化。
(7)、对对象进行对应的构造函数初始化。
(8)、将内存地址赋给栈内存中的p变量。
8、面向对象(对象调用成员过程)
上一个例子的Person类。 然后在PersonDemo类中输入以下代码:
public static void main(String[] args)
{
Person p = new Person ("ZhangSan",20);
p.setName = ("lisi");
}
以上代码的示图如下:
9、面向对象(单例设计模式)
设计模式:一种思想,解决某一类问题最行之有效的方法,Java中有23种设计模式,其中一种叫单例设计模式。 单例设计模式:解决一个类在内存只存在一个对象,想要保证对象唯一。
(1)、为了避免其他程序过多建立该类对象,先控禁止其他程序建立该类对象。
(2)、还为了让其他程序可以访问到该类对象,只好在本类中,自定义一个对象。
(3)、为了方便其他程序对自定义对象的访问,可以对外提供一些访问方式。
以上三步怎么用代码实现呢?
(1)、将构造函数私有化。
(2)、在类中创造一个本类对象。
(3)、提供一个方法可以获取到该对象。
代码:
Eg1:
class Single
{
private Single(){}
private static Single s = new Single();
public static Single getInstance()
{
return s;
}
}
class SingleDemo
{
public static void main(String []args)
{
Single s1 = Single.getInstance();
Single s2 = Single.getInstance();
}
}
以上代码示图如下:
以下是在Eg1的基础上改动的两个测试例子。
Eg2:
class Single
{
private int num;
public void setNum(int num) //公共方法setNUm,为私有变量num赋值
{
this.num = num ;
}
public int getNum() //获得当前num的值
{
return num ;
}
Single(){} //定义构造函数,默认是公共的
private static Single s = new Single(); //新建一个私有静态的Single类对象。
public static Single getInstance() //静态定义一个Single类型的方法getInstance,返回对象地址。
{
return s;
}
}
class SingleDemo
{
public static void main(String []args)
{
Single s1 =new Single(); //新建一个Single类对象
Single s2 =new Single(); //新建一个Single类对象
s1.setNum(30); //由于Single类中构造函数没有私有,所以对象s1调用
//setNum方法给num赋值30并不会影响到s2中的num变量
System.out.println(s2.getNum()); //因此输出s2中的num是0.因为没赋值。
}
}
输出结果:0
Eg3:
//由上例出现的问题,我们可以给Single类中构造函数Single(){}私有化,禁止主函数新建对象,那么就不会出现上例中的问题。
class Single
{
private int num;
public void setNum(int num) //公共方法setNUm,为私有变量num赋值
{
this.num = num ;
}
public int getNum() //获得当前num的值
{
return num ;
}
private Single(){} //定义构造函数,并定义为私有
private static Single s = new Single(); //新建一个私有静态的Single类对象。
public static Single getInstance() //静态定义一个Single类型的方法getInstance,返回对象地址。
{
return s;
}
}
class SingleDemo
{
public static void main(String []args)
{
Single s1 =Single.getInstance(); //定义一个Single类的变量,把Single类中自定义对象指向s1
Single s2 =Single.getInstance(); //定义一个Single类的变量,把Single类中自定义对象指向s2
s1.setNum(30); //由于s1和s2都指向了同一个地址,因此s1调用
//setNum给num赋值30,s2再调用getNum获得的num也就是30
System.out.println(s2.getNum()); //输出是30
}
}
输出结果:30
对于事物该怎么描述,就还是怎么地去描述,当需要将事物的对象保证在内存中唯一时,就将以上单例设计模式的三步加在类中即可。
Eg:
//用单例设计模式方法设计一个Student类、
class Student
{
private int age; //定义私有的整型age
private static Student s = new Student(); //类中建立新的Student类对象s
private Student(){} //把构造函数私有化
public static Student getStudent() //获得类中对象的地址方法
{
return s;
}
public void setAge(int age) //方法setAge为类中的成员变量age赋值
{
this.age = age ;
}
public int getAge() //方法getAge返回类中成员变量age
{
return age;
}
}
class SingleDemo
{
public static void main(String []args)
{
Student s1 = Student.getStudent(); //定义Student类变量,把Student类中的对象地址赋值给s1
Student s2 = Student.getStudent(); //定义Student类变量,把Student类中的对象地址赋值给s2
}
}
10、面向对象(单例设计模式方法2)
前一节所说的单例设计模式也称为饿汉式,即使先初始化对象。 模板:
class Single
{
private static Single s = new Single();
private Single(){}
public static Single getInstance()
{
return s;
}
}
而这节所分析的另一种方法,单例设计模式方法2,也称之为懒汉式,即对象是方法被调用时,才初始化,也叫做对象的延时加载。 模板:
class Single
{
private static Single s = null;
private Single(){}
public static Single getInstance()
{
if(s==null) //即如果s变量不是空,那么就新建一个对象,并把地址赋值给s变量,并返回。
{
s = new Single();
}
return s;
}
}
饿汉式和懒汉式的区别: 饿汉式:Single类一进内存,就已经创建好了对象 懒汉式:Single类进内存,对象还没有存在,只有调用了getInstance方法时,才建立对象。