黑马程序员_六 【final】【static】【内部类】【main】

本文详细探讨了Java编程中静态关键字与内部类的使用方式、注意事项及应用场景,包括静态变量、静态方法、静态导入、静态代码块、内部类的定义与访问规则,以及如何利用内部类简化代码实现。同时,介绍了静态类和内部类的基本概念、特点与区别,提供了实际编程案例以加深理解。

--------------------- android培训java培训、java学习型技术博客、期待与您交流! -------------------


 1  final 关键字

1 定义常量:可以在声明的同时赋值,也可以在构造函数或在代码块中赋值;

如:public final int num;

//代码块赋值

{

num=0;

}

类名()//构造方法赋值

{

num=0;

}

2 为节省内存 通常将常量声明为静态的。

如:static final doubole PI=3.1415926; 

若定义为静态,则应在初始化时初始化 或 在静态代码块中初始化。

|--对于原始类型,一旦赋值,不可改变。若不加static 同样属于成员变量,属于对象, 一个对象里面有一个。

|--对于引用型,赋值之后将不能再引用其他对象,但可以修改其引用对象的变量。是最 终引用。

|--final的变量不会默认初始化,需要显式初始化:声明时初始化 或 在构造方法中进行 初始化 或 在非静态语句块总进行初始化。

|--静态最终成员  final static public int num=2;需要声明时进行初始化 或 在静态 代码块中进行初始化

|--final的类不能被继承,里面的方法默认final

|--final的方法可以被继承但不能被重写

|--目的1 不想让子类复写,功能独有

|--目的2 编译器在遇到调用final方法时会转入到内嵌机制,大大提高执行效率

|--final 不用于修饰构造函数

 2  static 静态详谈

1. static特点

  |-- static修饰的直接被类名调用

  |-- 存储在方法区中的静态区

  |-- 优先于对象的加载而加载

  |-- 静态不能直接访问非静态

     |-- 先进内存的,不能访问后进内存的,后进的可以访问先进的

  |-- 静态方法中,不能写this,也不能写super

  |-- 非静态方法,可以直接调用静态变量

  |-- 对象全能

2. 静态变量和非静态变量的区别

  |-- 静态存储在方法区的静态区中

  |-- 非静态跟对象在堆内存中存储

  |-- 静态所属于类,为类变量,

  |-- 非静态所属于对象,实例变量,

  |-- 静态跟对类在方法区存储,类最先进内存,最后出去,生命周期最长

  |-- 非静态,跟随对象的存在而存在,跟对象的消失而消失,生命相对较短

  |-- 静态是所有对象共享的数据

  |-- 非静态是对象的特有数据

  |-- 静态变量存在静态区,有默认值

  |-- 非静态跟随对象在堆中存储,有默认值

3. 静态的弊端

  |-- 随着类的加载而加载,生命长,占用资源大

  |-- 静态不能访问非静态,造成访问的局限性

4. 静态的好处

  |-- 类名直接调用,不用写对象

  |-- 数据共享

5. 什么时候用静态

  |-- 静态变量

|-- 出现了共享数据,将变量定义成静态

|--当需要对创建类的个数进行计数时,可以定义静态计数器变量

  |-- 静态函数

    |-- 当类中的函数,没有访问过,类中的非静态变量的时候

        函数中除了自己的局部变量以为就是在访问类中的静态变量的时候

这个函数建议定义成静态函数,如:

Arrays.sort(arr);

Arrays.binarySearch(arr,4);

Integer.toHexString();

Integer.MAX_VALUE;

System.in

静态特点例子:

class Person
{
      String name;//成员变量,实例变量。没有被静态修饰,对象建立前不存在
      static String country = "CN";//静态的成员变量,属于类变量。因为每个Person都有CN的国籍,所以直接把这个变量被静态修饰,这样被所用对象共享,随着类的加载而加载并且优先于对象存在(对象建立前就有,建立后随着对象加载而加载),可以直接被类名调用
      public static void show()//被静态修饰的方法。为什么可以用静态修饰,因为功能内部没有访问到非静态数据(对象的特有数据,如不同人的名字),如果下面的输出语句里面有name值的输出,函数就不能用static修饰
      {
           System.out.println("::::");//括号内不能+name如("::::"+name),这样语法错误。因为name默认为this.name,this.super语句不能用于静态语句中。原因是静态在String name之前存在于方法区之中,不能调用非静态的变量。
           //this.haha();语法错误,因为this.super语句不能用于静态语句中。
      }
      public void haha()
      {}
}
 
class  StaticDemo
{
      public static void main(String[] args)
      {
           Person p = new Person();
           //p.name = "zhangsan";
           //p.show();
           //System.out.println(p.country);
           System.out.println(Person.country); //没有对象,但是county是静态变量,所以可以直接被类名调用。
           Person.show();//匿名函数调用
      }
}


静态导入:

静态导入是转对于工具类的,如java类库中的ArraysCollections等,里面的方法都是静态的,可以用类名世界调用,但是需要导入包,为了更方便的使用里面的方法,使用静态导入。

格式:import static java.util.Collections.*;

这样就可以直接使用里面的方法了。

静态运用--工具类:

以操作数组为例,创建一个工具类ArrayTool

每一个应用程序中都有共性的功能,可以将这些功能进行抽取,独立封装。以便复用。

虽然可以通过建立ArrayTool的对象使用这些工具方法,对数组进行操作。

但是发现了问题:

1,对象是用于封装数据的,可是ArrayTool对象并未封装特有数据。

2,操作数组的每一个方法都没有用到ArrayTool对象中的特有数据。

这时就考虑,让程序更严谨,是不需要对象的。

可以将ArrayTool中的方法都定义成static的。直接通过类名调用即可。

public classArrayTool
{
      private ArrayTool(){}//私有化构造函数,不让外界使用。将方法都静态后,可以方便于使用,但是该类还是可以被其他程序建立对象的。为了更为严谨,强制让该类不能建立对象。可以通过将构造函数私有化完成。记住这个私有化类的格式,是用构造函数来做的。
      public staticint getMax(int[] arr)//定义一个数组求最大值。被修饰成静态是因为没有访问到对象的特有属性。好处是随着类的加载而加载,优先于对象存在,被所有对象所共享,可以直接被类名调用。
      {
           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)// 每一个应用程序中都有共性的功能,可以将这些功能进行抽取,独立封装。以便复用。
      {
           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)
      {
           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)
      {
           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)
      {
           int temp = arr[a];
           arr[a] = arr[b];
           arr[b] = temp;
      }
      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]+"]");
           }
      }
}
接下来,将ArrayTool.class文件发送给其他人,其他人只要将该文件设置到classpath路径下,就可以使用该工具类。直接调用,如ArrayTool.getMax(arr);可以直接调用
classArrayToolDemo
{
      public static void main(String[] args)
      {
           int[] arr = {3,1,87,32,8};
           int max = ArrayTool.getMax(arr);
           System.out.println("max="+max);
           /*
           ArrayTool tool = new ArrayTool();
           int max = tool.getMax(arr);//上一个演示代码中构造函数ArrayTool()已经被private私有化,不能简历对象了,所以这样的语句会编译失败。只能直接使用匿名函数调用ArrayTool.getMax(arr);
           System.out.println("max="+max);
           int min = tool.getMin(arr);
           System.out.println("min="+min);
           tool.printArray(arr);
           tool.selectSort(arr);
           tool.printArray(arr);
           int[] arr1 = {};
           ArrayTool tool1 = new ArrayTool();
           */
      }
}


如果编译直接ArrayToolDemo,没有找到ArrayTool,系统会自动在本文件夹内,自动找ArrayTool.class文件自动编译。不在本文件夹,就得设置临时变量,找到ArrayTool,不然不能运行。

但是,很遗憾,该类中到底定义了多少个方法,对方去不清楚。因为该类并没有使用说明书。

开始制作程序的说明书APIjava的说明书通过文档注释来完成。

说明书的符号为

/**

文档简介或者使用说明

@author  张三

@version  v1.1

*/

编译命令

javadoc d  文件目录文件名   

编译后,说明书的内容就会自动生成一个网页文档。

注意想要生成编译帮助文档,类必须是被public修饰的。没有共有的类,是没有被暴露的,没法对外生成帮助文档。

静态代码块:

格式:

Static

{

      //静态代码块中的执行语句;

}

特点:随着类的加载而执行,只执行一次,并优先于主函数。用于给类进行初始化的。

 

class StaticCode
{
      int num = 9;
      StaticCode()
      {
           System.out.println("b");
      }
      Static////静态代码块,随着类的加载而执行,只执行一次,并优先于主函数,目的是给类初始化的。
      {
           System.out.println("a");
      }
      {//此条代码直接运行,在静态代码块运行后直接运行。属于构造代码块,给对象初始化的
           System.out.println("c"+this.num);
      }
      StaticCode(int num)//属于构造函数,是给对应对象初始化的~
      {
           System.out.println("d"+num+this.num);
      }
      public static void show()
      {
           System.out.println("showrun");
      }
}
classStaticCodeDemo
{
      Static//静态代码块
      {
           System.out.println("b");
      }
      public static void main(String[] args)
      {//当主函数运行是,就会运行静态代码块,输出b,c
           //new StaticCode();//运行静态代码块,输出a
           //new StaticCode();////运行静态代码块,输出a
           //System.out.println("over");//最后输出的结果就是b c a a over
           //StaticCode.show();只运行这个指令输出的是b c a和show(),不会运行构造代码块输出c,因为没有建立对象
           //StaticCode s = null;当设置对象为空时,是不会运行静态数码块的,因为没有对象,没有在堆内存中建立空间。这时候没有什么输出,因为只建立了对象,没有调用对象,不会执行构造代码块
           //s = new StaticCode();对象建立,内存空间会分配,会运行静态数码块。先输出静态代码块a,然后构造代码块,c9,然后是构造函数,b。最后输出结果为b c a c9 b。
           //StaticCode.show();//输出结果是b c a show time。
new StaticCode(4);//输出的结果是b c a c9 d49,虽然有4作为构造函数传入了进去,但是会优先执行构造代码块输出的c和this.num,所以是9.然后执行构造代码块,num是4了,this.num还是9所以是c49
      }
      static//静态代码块
      {
           System.out.println("c");
      }
}


知识补充:

堆区
1.存储的全部是对象,每个对象都包含一个与之对应的class的信息。(class的目的是得到操作指令
2.jvm只有一个堆区(heap)被所有线程共享,堆中不存放基本类型和对象引用,只存放对象本身 
栈区
1.每个线程包含一个栈区,栈中只保存基础数据类型的对象和自定义对象的引用(不是对象),对象都存放在堆区中 
2.每个栈中的数据(原始类型和对象引用)都是私有的,其他栈不能访问。 
3.栈分为3个部分:基本类型变量区、执行环境上下文、操作指令区(存放操作指令)。 
方法区: 
1.又叫静态区,跟堆一样,被所有的线程共享。方法区包含所有的classstatic变量。 
2.方法区中包含的都是在整个程序中永远唯一的元素,如classstatic变量。 

 3  内部类

将类定义在另一个类的内部,叫做内部类。

|--由来:
分析事物时,发现该事物描述中还有事物,而且这个事物还在访问被描述的内容,"还有的事物"就用内部类来描述。

|-- 内部类可以访问所在外部类的私有成员。

|-- 内部类加上接口 可以很好的实现多继承的效果。

|-- 内部类一个主要的用途,GUI用户图形界面设计的事件处理。

|-- main方法所在的内部类 不能在main方法中创建对象,main方法是静态的,内部类作 为成员是非静态的,在静态方法main中不能用非静态的内部类。
|-- .class文件
┗━编译之后,内部类也会产生.class 文件,名称形式:Outer$Inner.class中间用$符号分隔开,表示所属关系。
┃--内部类之所以可以访问外部类的所有成员,内部类和外部类对象有 
this reference 关系。在内部类当中有隐式的Outer.this引用,所以可以直接用,相当于有外部类对象一样,可以直接使用外部类成员。
┃--如果在局部有同名成员,那么就近原则,若想访问自身的,就用this.引用,若想访 问外部的就用Outer.this.去引用其成员变量。若要在main方法中使用inner类成员。
┗━理解:内部类 相当于是所属外部类的成员,所以要先有外部类对象再去调用成员,所以要现有new Outer.先创建所属外部类对象。
可以:Outer out=new Outer();
Inner in=out.new Inner();//分布写
一个式子:Outer.Inner inner = new Outer().new Inner();


┃--所属外部类、内部类、内部类局部 三种变量访问:
┃--局部:直接访问名字
┃--内部类:this.+变量名,访问内部类成员变量,Inner.this.+变量名 结果也是内部类的成员变量。
┃--所属外部类:同理:Outer.this.+变量名,得到的是所属外部类的成员变量。


┃--如果内部类在局部位置上
┃--内部类 访问局部变量: 内部类只能访问被 final 修饰的局部变量。
final为常量,生命周期长。不然,变量随着进栈用完后就出栈了,在用内部类对象去调用该变量就调用失败了。比如,在方法中的内部类,在方法中创建了一个内部类对象并return这个对象,用Object obj在main中接收(多态),用过之后,再用obj调用局部变量时,就找不到该变量了,因为已经出栈了。
|--简单说就是局部变量 和 对象的生存周期不同。


┃--内部类可以声明在方法中,也可以放到 if 语句当中,还可以把内部类的定义放到     语句块 中。不管内部类 嵌套的层次多深,都可以访问所在外部类的成员。


┃--内部类可以被声明为 private ,和修饰方法一样。内部类 也可以是 抽象 的。可以 是静态的static 。也可以是final。也可以是protected。


┃--非静态 内部类 中不能有 静态成员的声明。****内部类中有静态成员时,内部类必 须是静态的。


┃--静态内部类 :static修饰就变成了顶层类,外部类一加载也跟着加载,创建内部类
对象:new Outer.Inner();// 相当于外部类类名直接调用静态成员。
┃-- 创建内部类对象格式:Outer.Inner Inter = new Outer.Inner();
┃-- 同样,静态内部类只能访问静态成员。
┃-- 静态内部类,有静态方法,调用:Outer.Inner.function();


┃--内部类被继承:被继承后,其子类需要调用它的构造方法,而内部类的构造方法的调 用需要先产生所在外部类的 对象,再产生内部类的对象,从而建立起引用关系。


例子1:

class Car
{
	class Wheel
	{
		
	}
}

class PlaneWheel extends Car.Wheel //继承 内部类
{
	PlaneWheel(Car car)
	{
		car.super(); //建立关系
	}

	public static void main(String[] args)
	{
		Car car=new Car();
		PlaneWheel pw=new PlaneWheel(car); //将对象引用作为参数
	}
}



内部类的特点:内部类可以直接访问外部类中的成员,而外部类要访问内部类的成员必须要建立内部类的对象.

内部类在不同的位置上具有不同的特性:内部类定义在成员位置上可以被private static修饰,被static修饰的内部类只能访问外部类中的静态成员;内部类定义在局部位置上,也可以直接访问外部类中的成员,同时也可以访问局部变量,但变量必须被final修饰.

例子2:

interface MyIner  
{  
public abstract void show();
}  
class MyOuter  
{  
    public void method()  
    {  
      
     MyIner my =  new MyIner()  
        {  
        	 public  void show(){ System.out.println("Hello world!");   
        };  
        my.show();  
    }  
} 


子类建立对象时 需要调用 父类(内部类)的构造方法,而内部类的构造方法被调用需要通过所在外部类的的对象,也就是需要建立关系。

匿名内部类  :

 格式:new 父类或接口(){子类要覆盖的方法},使用前提:内部类必须是继承一个类或者实现接口,匿名内部类其实就是建立一个建立一个带内容的外部类或者接口的子类匿名对象.

实质:是一个类或接口的 匿名内部子类的对象,用法是这样。
匿名内部类是内部类的简写形式。
使用前提:
内部类必须继承 或者 实现一个外部类或者接口。不然创建对象没名字就出错。
想继承 或者 实现谁,创建匿名内部类就用那个父类 或者 父接口。

就是用父类 或 接口区创建一个子类或子接口,必须要先存在一个类或接口,再用这个类或者接口去创建子类或子接口。

比如,一个抽象类,里面有抽象方法,
格式:new 抽象类(){实现抽象方法(子类内容)}.实现的方法(); 
就是子类对象调用实现的方法。这就是匿名内部类。


没有名字 可以用父类 或 父接口创建的对象引用 接收:
Outer outer = new Outer()
{ 匿名子类内容;  }
这样就可以用了。利用的多态。


万能匿名内部类:new Object()
  { 子类内容 }.子类方法();


匿名内部类.class 文件:Outer$1.class....Outer$2.class 用阿拉伯数字按顺序生成。


匿名内部类在图形界面设计中比较常用,添加一个组件,可以直接用匿名内部类的形式添加。注册一个事件,可以也能够匿名内部类的形式简化书写,因为,只需要用到一个方法。集合里面的比较器,也可以用匿名内部类的形式,把匿名内部类当做参数传递给需要用比较器的集合构造方法。

如:

new TreeMap(
new Comparator()
{
	public int compare(Object obj1,Object obj2)
	{
		//实现代码;
	} 
});


使用非常方便,也非常灵活。

 4  main解析

主函数:是一个特殊的函数。作为程序的入口,可以被jvm调用。

主函数的定义:

|-- public 权限修饰符,main被JVM调用,main方法的权限必须足够大

|-- static 这个main属于这个类,不需要对象,直接类名调用

|-- void   没有返回值,JVM调用main方法,如果有的话,就返回给了 JVM

|-- main   你就不能改,虚拟机就认识main

|--(String[] args) 这是一个字符串数组参数,调用main的JVM会不会 传递参数进来

|-- main 以后只做一件事情,开启程序

String[] args:函数的参数,参数类型是一个数组,该数组中的元素是字符串。字符串类型的数组。args是名字,写这个是因为早期写arguments,简写成args。这个是可以改的。因为大家都这样写,为了可读性,就写成args了。

主函数是固定格式的:目的是被jvm识别。Jvm只识别这个

class MainDemo
{
      public static void main(String[] args)//args是一个数组。
      {
           System.out.println(args);//输出的是一个哈希值,显示的是args在内存中的地址值。里面每一个值都是null
System.out.println(args.length);//输出数组的长度,为0
//由此可见,main被调用的时候,jvm在调用主函数时,传入的是new String[0]
           String[] arr ={"hah","hhe","heihei","xixi","hiahia"};//给数组装数据。
           MainTest.main(arr);//执行MainTest中的main函数
      }
}
class MainTest
{
      public static void main(String[] args)//虽然有两个主函数,但是在两个类中,所以语句合法
      {
           for(int x=0; x<args.length; x++)
                 System.out.println(args[x]);//依次打印出数组中的每个数组值。
      }
}


本博文学习总结:通过记录本博文,更加熟悉了静态的使用和内部类的使用,以及他们的注意事项。静态最常用的特点:随类加载,类名调用等,是必须要牢牢掌握的。还有内部类作为参数传递--匿名内部类,以及内部类使用的注意事项,也是非常重要的。


  本篇博文结束!




                                                                                                   @感谢老师的辛苦批阅




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值