一、面向对象—— static 关键字
1、用法: static(静态)是一个修饰符,用于修饰成员(成员变量和成员函数); 2、被修饰后的成员具备以下特点: A、随着类的加载而加载; 类需要被使用时,需要进内存;类进内存时,静态就已经存在了。反过来,静态随着类的消失而消失,其生命周期最长。 B、优先于对象存在; 静态先存在,对象后存在。 C、被所有对象所共享;(static成员存在于方法区中)(非静态内容是特有内容,特有内容随着对象存储在堆内存当中) D、可以直接被类名调用。(调用方法:类名.静态成员) 3、使用注意: A、静态方法只能访问静态成员;非静态方法既可以访问静态成员,也可以访问非静态成员。 B、静态方法中不可以写 this, super 关键字;因为 static 优先于对象存在,this 代表的是对象。 C、主函数是静态的。 4、静态的优缺点: 优点:对对象的共享数据进行单独空间的存储,节省空间;可以直接被类名调用。 弊端:生命周期太长;访问出现局限性(只能访问静态)。 5、关于方法区: 又叫共享区,数据区; 存储的数据包括:类中的方法,类中的共享数据(static是共享数据)。 6、小知识: (1)成员变量,又叫实例变量; 静态成员变量,又叫类变量。 (2)实例变量和类变量的区别: A、存放位置: 类变量随着类的加载而存在于方法区中; 实例变量随着对象的建立而存在于堆内存中。 B、生命周期: 类变量生命周期最长,随着类的消失而消失; 实例变量生命周期随着对象的消失而消失。 ======类加载后,什么时候消失??这个过程比较复杂,请百度。 代码:class StaticDemo { public static void main(String[] args) { Person p = new Person(); p.name = "lisi"; p.show(); System.out.println(Person.country);//直接用类名调用 } } class Person { String name;//成员变量,实例变量 static String country = "CN";//静态的成员变量,类变量。被静态修饰后,数据存放在方法区中。 public static void show() { System.out.println(country); //System.out.println(name+"::"+country);不能访问非静态的name } }
二、面向对象——main函数
public static void main(String[] args)
1、主函数:是一个特殊的函数。作为程序的入口,可以被虚拟机调用。
2、主函数的定义:
public 代表这该函数访问权限是最大的;
static 代表主函数随着类的加载就已经存在了;
void 代表主函数没有具体返回值;
main 不是关键字,但是是一个特殊的单词,可以被虚拟机识别;
(String[] args) 代表函数的参数,参数类型是一个数组,该数组中的元素是字符串。args是arguments的缩写,是一个变量名。
3、主函数的格式是固定的,其格式除了变量名args可以改变,其他内容都不允许改变,否则虚拟机不识别。
4、任何程序调用非空参数函数都需要传递一个参数给该函数。
因此,虚拟机调用主函数时,也给主函数传递一个参数,该参数是一个数组实体 new String[0] ,该数组的长度是0;
验证代码:class Demo { public static void main(String[] args) { System.out.println(args);//[Ljava.lang.String;@15db9742//这是一个数组的内存地址值 System.out.println(args.length);//长度为 0 } }
5、javac命令是启动编译器;
java命令调用的是虚拟机,虚拟机执行类,类中的方法被虚拟机调用。
java在启动虚拟机,执行类的同时,可以给其传递参数;java执行类时,需要将类传递给虚拟机,在执行类的同时,调用类中的main方法。
例如:
启动虚拟机时,输入 java MainDemo haha hehe heihei (其中MainDemo是可独立运行的类)
表示将字符串haha hehe heihei 作为参数传递给主函数,并将其存入数组。
参考代码:在命令行中输入java MainDemo haha hehe heihei与直接运行MainTest结果一样。class MainTest { public static void main(String[] args) { String[] arr = {"haha","hehe","heihei"}; MainDemo.main(arr); } } class MainDemo { public static void main(String[] args) { for (int x=0 ; x<args.length ; x++ ) { System.out.println(args[x]); } } }
三、面向对象——静态什么时候使用
静态修饰的内容有成员变量和成员函数
什么时候定义静态成员变量(类变量)?
当对象中出现共享数据时,该数据就用static修饰;
对象中的特有数据要定义成非静态,使其存在于堆内存中。
什么时候定义静态成员函数?
当功能内部没有访问到非静态数据(对象的特有数据),那么该功能可以定义成静态;
当功能内部访问到特有数据时,不能定义成静态。四、面向对象——静态的应用——工具类
在开发当中,每一个应用程序中都有共性的功能,可以将这些功能进行抽取,独立封装,以便复用。
例如对数组的常见操作,可以将这些操作的功能都独立封装到一个类中,并将这些功能都用静态修饰,这个类可以叫做数组的工具类。
同时,为了防止建立工具类的对象,可以将工具类的构造函数私有化。
这时,要对数组进行操作时,就能用类名调用方法。
工具类中的方法都没有用到该类中的特有数据(非静态成员),所以不需要创建对象,所以可以将构造函数私有化。
代码见下一节。五、面向对象——帮助文档的制作javadoc
将做好ArrayTool.class文件发送给其他人,其他人只要将文件设置到classpath路径下,就可以使用该工具类。但是,该工具类需要制作文档说明书。
配置classpath环境变量方法:
set classpath=.;指定路径
//先在当前路径找,再到指定路径下找
1、文档注释: /** 注释文字 */
2、注意:文档注释中有一些特殊的标识,可以直接被文档注释工具所提取并识别。
比如:
用于注释类的标识:
@author 作者
@version 版本
用于说明程序的类:
@param
@return
文档注释写完,可以用 javadoc 工具编译源文件,生成说明文档。该文档将以网页的形式体现。(doc就是documents)
doc命令行中编译代码示例:
javadoc -d myhelp -author -version ArrayTool.java
//-d 表示存放路径,myhelp表示路径名为当前路径下的myhelp文件夹。
//如果没有加 -author 和 -version ,生成的说明文档将不体现作者和版本。
3、注意:
A、想要把一个类编译成说明文档,这个类必须用 public 修饰。
B、只有被 public 修饰的方法和构造函数才体现在文档当中。
C、一个类中有一个默认的空参数的构造函数,该构造函数的权限和所属类一致。
API(Application Programming Interface,应用程序编程接口)是一些预先定义的函数,目的是提供应用程序与开发人员基于某软件或硬件得以访问一组例程的能力,而又无需访问源码,或理解内部工作机制的细节。(来自网络释义)
示例代码:<pre name="code" class="java"> /** 这是一个可以对数组进行操作的工具类,该类中提供了,获取最值,排序等功能。 @author 张三 @version V1.1 */ public class ArrayTool { /** 私有空参数构造函数。 */ private ArrayTool(){} /** 获取一个数组中的最大值。 @param arr 接收一个int类型的数组。 @return 返回一个该数组中最大值。 */ public static int getMax(int[] arr) { 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) { int min = 0; for(int x=1; x<arr.length; x++) { if(arr[x]<arr[min]) min = x; } return arr[min]; } /** 给int类型数组进行选择排序。 @param arr 接收一个int类型的数组。 */ public static void selectSort(int[] arr) { 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); } } } } /** 给int数组进行冒泡排序。 @param arr 接收一个int类型的数组。 */ public static void bubbleSort(int[] arr) { 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) { int temp = arr[a]; arr[a] = arr[b]; arr[b] = temp; } /** 用于打印数组中的元素。打印形式是:[elemet1, element2, ...] */ public static void printArray(int[] arr) { System.out.print("["); for(int x=0; x<arr.length; x++) { if(x!=arr.length-1) System.out.print(arr[x]+", "); else System.out.println(arr[x]+"]"); } } }
</pre><h3>六、面向对象——静态代码块</h3><span style="white-space:pre"></span>1、格式:<span style="white-space:pre"></span>static<span style="white-space:pre"></span>{<span style="white-space:pre"></span>静态代码块中的执行语句;<span style="white-space:pre"></span>}<span style="white-space:pre"></span>2、特点:<span style="white-space:pre"></span>随着类的加载而执行,只执行一次,并优先于主函数执行。<span style="white-space:pre"></span>3、作用:<span style="white-space:pre"></span>用于给类进行初始化。<span style="white-space:pre"></span><span style="white-space:pre"></span>4、注意:<span style="white-space:pre"></span>当使用到类中的内容时,类才会被加载;类被加载,静态代码块才会被执行。<span style="white-space:pre"></span>5、执行顺序:<span style="white-space:pre"></span>静态代码块先执行,然后执行构造代码块,然后才执行构造函数。<span style="white-space:pre"></span>//静态代码块给类初始化,构造代码块给所有对象初始化,构造函数给对应对象初始化。<span style="white-space:pre"></span>6、访问原则:<span style="white-space:pre"></span>A、静态代码块先进内存,所以不能访问成员变量;<span style="white-space:pre"></span>B、构造代码块随着是给对象初始化的,可以访问成员变量。<span style="white-space:pre"></span>示例一:<pre name="code" class="java"> class StaticCodeDemo
{
static
{
System.out.println("a");
}
public static void main(String[] args)
{
new Test();
new Test(3);
}
static
{
System.out.println("b");
}
}
class Test
{
int num = 9;
static
{
System.out.println("c");
}
{
System.out.println("d"+num);
}
Person()
{
System.out.println("e");
}
Person(int x)
{
System.out.println("f"+x);
}
}
//打印结果:a b c d9 e d9 f3
只要用到类中的内容时,静态代码块就执行,比如创建对象(创建对象时使用了类中的构造函数);
但是并不一定要创建对象,静态代码块才执行:
class StaticCodeDemo2
{
public static void main(String[] args)
{
Test.run();
Test t = null;//注意:当只有这一句代码时,Test中的static代码块不会执行。
}
}
class Test
{
static
{
System.out.println("A");
}
public static void run()
{
System.out.println("run");
}
}
//打印结果:A run
七、面向对象——对象的初始化过程
示例:Person P = new Person("zhangsan",20);
左边在栈内存中建立 Person类类型的变量 p ;
右边:
1,因为 new 用到了Person.class.所以虚拟机会先找到Person.class文件并加载到内存,并开辟堆内存空间;
2,执行该类中的static代码块(如果有静态代码块的话)给Person.class类进行初始化;
3,在堆内存中开辟空间,分配内存地址;
4,在堆内存中建立对象的特有属性。并进行默认初始化(name=null,age=0);
5,对属性进行显示初始化(定义属性时的赋值叫显示初始化),(该类中定义属性时没有赋值);
6,对对象进行构造代码块初始化;
7,对对象进行对应的构造函数初始化(这时才将传进来的值赋值给对象属性),(name=zhang,age=20);
8,将内存地址付给栈内存中的p变量。
然后左边的p变量指向右边的对象。
代码:
class PersonDemo
{
public static void main(String[] args)
{
Person p = new Person("zhangsan",20);
p.speak();
}
}
class Person
{
private String name = "none";
private int age = 10;
private static String country = "CN";
{
System.out.println(name+" : "+age+" & "+country);
}
Person(String name, int age)
{
this.name = name;
this.age = age;
}
public static void showCountry()
{
System.out.println("Country: "+country);
}
public void speak()
{
System.out.println("speak: "+name+" & "+age);
}
}
//结果none : 10 & CN speak: zhangsan & 20
八、面向对象——对象调用成员过程
1、栈内存中,主函数先进栈内存,定义一个变量p;方法区中,先加载静态成员和非静态方法;堆内存中,建立Person("zhangsan",20)变量,同时将这个变量空间的地址值赋值给栈内存中的p;
2、p.setName("lisi");栈内存加载setName方法,定义name变量,以及this的引用;然后变量p将获得的地址值赋值给this,this就指向了Person对象;再次,this将 "lisi" 赋值给对象的name.
静态方法调用过程:
showCountry()方法被调用时,没有指向,所属于Person类,所以可以直接用类名调用showCountry();而且showCoutry()方法调用时,访问的都是静态区中的数据。
示例:
class Person
{
private String name;
private int age;
private static String country = "cn";
Person(String name,int age)
{
this.name=name;
this.age=age;
}
public void setName(String name)
{
this.name = name;
}
public void speak()
{
System.out.println(name+"..."+age);
}
public static void showCountry()
{
System.out.println(person.country);//Person. 可以省略
Person.method();//Person. 可以省略
}
public static void method()
{
System.out.println("method");
}
}
class PersonDemo
{
public static void main(String[] args)
{
Person p = new Person("zhangsan",20);
p.setName("lisi");
}
}
九、面向对象——单例设计模式
1、设计模式:解决某一类问题最行之有效的方法。java中有23中设计模式。2、单例设计模式:解决一个类在内存中只存在一个对象的方法。
3、单例设计模式思想——保证对象唯一:
A、为了避免其他程序过多建立该类对象,可以将该类的构造函数 private;
B、其次,还为了让其他程序可以访问该类对象,只好在本类中自定义一个本类对象;
C、为了方便其他程序对自定义对象的访问,可以提供一个方法获取到该对象,对外提供该访问方法。
单例的用法:
对于事物该怎么描述,还怎么描述;当需要将该事物的对象保证在内存中唯一时,就加上以上三步即可。
示例:
class Single
{
private Single(){}//私有构造函数,避免其他程序建立该类对象
private static final Single s = new Single();//该成员不需对外暴露,需用private隐藏;该成员需被静态方法调用,所以需static修饰。//用final修饰,表示为最终变量。
public static Single getInstance()//其他程序不能通过建立本类对象调用该方法;所以只能将该方法static,以方便类名调用。
{
return s;
}
}
class SingleDemo
{
public static void main(String[] args)
{
Single s1 = Single.getInstance();
Single s2 = Single.getInstance();
}
}
该程序内存中只有一个对象,该对象有三个引用在指向: s s1 s2
十、单例设计模式方式二
1、懒汉式:对象的延时加载 class Single
{
private Single(){}
private static Single s = null;
public static Single getInstance()//synchronized
{
if(s==null)//此处存在安全隐患
s = new Single();
return s;
}
}
class SingleDemo
{
public static void main(String[] args)
{
Single s1 = Single.getInstance();
}
}
2、饿汉式和懒汉式的区别:
饿汉式:Single类一进内存,就已经创建好了对象;
懒汉式:Single类进内存,对象还未存在,只有调用了getInstance方法时,才建立对象。
3、注意:定义单例时,常用饿汉式,开发安全。
懒汉式的安全写法:
class Single
{
private Single(){}
private static Single s = null;
public static Single getInstance()
{
if(s==null)
{
synchronized(Single.class)
{
if(s==null)
s = new Single();
}
}
return s;
}
}