特殊关键字
特殊关键字
包package是类或接口的容器
什么是包
- 包是类的组织方式,提供名空间和访问控制
- 类似于文件系统的组织
声明包中的类package
语法:package 包名称;
-
声明包要求是源代码文件的第一个语句
-
程序中最多只能有一条package语句,没有包定义的源代码文件成为默认包中的一部分
-
程序中如果有package语句,该语句必须是第一条语句(前面只能有注释或空行)
包的作用
- 包能将相关的源代码文件组织在一起
- 包减少了名称冲突带来的问题
- 可以使用package指明源文件中的类属于哪个具体的包
包的使用
- 如果其他人想使用包中类,则需要使用全名
- 为了简化书写,Java提供了import语句: import package_name.*;
- 引入包中的某一个类:import java.util.ArrayList;
引入包import
- Java缺省引入的包为java.lang
- 引入包中的类: import 包名称.类名称;例如import java.util.Date;
- 引入整个包; import 包名称.* ;例如 import java.util.* ;
- 不引入包而是用类,则需要是用类的全名: 包名.类名,例如java.util.Date dd=new java.util.Date();
包的文件组织和类路径CLASSPATH
在类路径中定义包的位置
- 代表当前目录,为默认值
- 类文件存放的位置:类路径中的目录\包名称对应的路径
- 缺省包default package
- 源代码文件中没有package语句声明,且类文件位于CLASSPATH所定义的目录中
- 满足以上条件的所有类被认为处在同一个包中,这个包被称为缺省包
Classpath描述了Java虚拟机在运行一个Class时在哪些路径中加载要运行的类以及运行的类要用到的类。当一个程序找不到他所需的其他类文件时,系统会自动到CLASSPATH环境变量所指明的路径中去查找第三方提供的类和用户定义的类
Java中常用的包
- java.applet用于提供Applet开发的支持,目前已经被 ash所替代,很少使用,只有在某些地图系统中还有使用
- java.awt和javax.swing用于单机软件或者c/s应用中的界面开发
- java.io用于输入、输出操作
- java.lang语言包,默认自动加载的包
- java.net用于网络编程
- java.util工具包,是Java提供的一些工具类
this和super关键字
this用于指代当前对象;super用于指代父类对象
- this()表示调用另外一个构造器
- super()表示调用父类中的某个构造器,()中的参数决定调用的是哪个构造器
public class A {
public A() {
//需要调用A(String)这个构造器方法
this("zhangsan");
}
public A(String name) {
System.out.println("name:"+name);
//调用当前类中的另外一个构造器
}
}
//另外的写法
public A(String name) {
this();
//在当前构造器中调用另外一个构造器方法的语句必须是构造器方法中的第一句
System.out.println("name:"+name);
}
//语法错误
public A(String name) {
if(name!=null)
this();
System.out.println("name:"+name);
}
//当定义构造器时,如果构造器方法中第一句不是this()或者super()时,默认第一句就是super()表示调用父类的构造器
public class B extends A {
public B() {
super();//表示调用父类的无参构造器
}
}
父类
public class A {
private A() {}
public A(int k) {}
}
子类
public class B extends A {
public B() {
super(123);
//如果不写则语法报错,因为默认会调用父类的无参构造器,但是父类的无参构造器为private,子类中不能访问;super(123)表示调用父类中的带参构造器public A(int k)
}
- this.成员属性用于表示当前对象的某个成员,一般用于局部变量和属性名称一致的场景下。super.成员属性用于表示父类中定义的某个属性,一般用于子类中覆盖定义了某个父类属性的场景下
public class A {
private String name;
public void setName(String name) {//局部变量名name和属性name重名,可以使用this.name表示这个是属性,按照就近原则,当前方法中的name是局部变量
this.name = name;
}
}
public class A {
String name="A类";
}
public class B extends A {
private int name=123;//和父类中的属性name重名,按照就近原则,当前类中的name就是子类中声明的name,而不是父类中声明的name
public void pp() {
System.out.println("子类中声明的name:"+name);//也可以写成this.name
System.out.println("父类中声明的name:"+super.name);//使用super.name引用父类中声明的name属性,还需要考虑范围限定词的问题
}
public static void main(String[] args) {
new B().pp();//new运算符的优先级高于点运算符
}
}
- this.成员方法()用于表示当前对象的某个成员方法;super.成员方法()用于表示当前类的父类中定义的某个成员方法,一般用于覆盖定义时【就近原则】
public class A {
public void pp() {
System.out.println("this is A class");
}
}
public class B extends A {
public void pp() {//子类中定义的方法签名和父类中的一致,实际上就是方法的覆盖
System.out.println("this is B class");
}
public void cc() {
//
this.pp();//调用B类中声明的方法pp
System.out.println("this is B.cc()");
super.pp();//调用父类中被子类同名方法覆盖的方法,注意范围限定词
}
public static void main(String[] args) {
new B().cc();
}
}
在static方法中不允许使用this/super之类关键字
public class A {
protected Long id;
public void pp() {
System.out.println("this is A class");
}
public static void main(String[] args) {
//
System.out.println(this.id);//典型的语法错误,在static方法中不允许出现this或者
super
A a=new A();
System.out.println(a.id);
}
}
public class B extends A {
private String id;
public void show() {
System.out.println(super.id);
}
public static void main(String[] args) {
B b=new B();
System.out.println(b.id);
b.show();
}
}
关键字 final
意思是最终的、不可变的
修饰属性
final修饰变量时,变量被必须初始化,并且不能被修改。初始化可以定义 nal变量时直接初始化或者在构造函数中初始化
public class A {
private final String name="123"; //使用final一般用于定义常量,可以在声明的同时进行赋值,例如PI=3.1415。一般常量的命名规则为全大写,下划线分词USE_NAME
public void pp() {
//name="abc";final表示name一旦赋值则不能修改
}
}
public class A {
private final String name;
public A() {
//定义final属性如果在构造器中赋值,则必须在所有当前类的构造器中赋值
name="123456";
}
public A(int k) {
name="rrrr";
}
public void pp() {
//name="abc";final表示name一旦赋值则不能修改
}
}
修饰局部变量
public class A {
public void pp() {
//final int kk=123;声明的同时直接进行赋值作
final int kk;//先声明后赋值
System.out.println("其它操作");
kk=222;
//kk++;final变量一旦赋值则不允许进行修改操作
System.out.println(kk);
}
}
修饰方法
final修饰方法则该方法不能被子类重写
public class A {
public final void pp() {
System.out.println("this is A class");
}
}
public class B extends A {
public void pp() {
//因为A类中的方法pp有final修饰,则该方法不允许覆盖定义,但是允许重载
System.out.println("this is B class");
}
public void pp(int k) {}
}
修饰类
final修饰一个类,则该类不能被继承
public final class A {
}
//因为A类是final类,所以不能被继承
public class B extends A {
}
通过private构造器也达到了类似的不允许继承的效果,但是要求类中的所有构造器都是私有
扩展
表示字串的3种类型String、StringBu er、StringBuilder都是 nal类型的类,所以都不允许继承
public class A extends String{}//语法错误
总结
final 属性上可以声明的同时直接赋值或者在构造器中赋值
public class A{
private final int kk;
public A(int counter){
kk=counter;
}
}
- final临时变量,可以声明的同时赋值或者在第一次使用之前进行赋值
注意: nal类型的变量一旦赋值则不允许修改,但是如果是复杂类型是不允许修改地址,但是可以修改属性 - final方法表示这个方法不允许在子类中重新定义(覆盖\重写)
- final类表示这个类不允许被继承
单例模式
意图:保证一个类仅有一个实例,并提供一个访问它的全局访问点。
主要解决:一个全局使用的类频繁地创建与销毁。
何时使用:当您想控制实例数目,节省系统资源的时候
单例模式有多种写法,最常见的是懒汉模式和饿汉模式
饿汉单例模式
优点:没有加锁,执行效率会提高
缺点:类加载时就初始化,浪费内存
编程实现:
- 私有构造器
- 静态的私有属性
- 公共的静态方法
懒汉单例模式
懒汉式:主要是针对饿汉式中不管是否需要使用对象都先创建对象所引起的内存浪费
优点:由于采用栏延迟处理的方式,所以比较节约内存
缺点:在多线程不能正常工作
静态static关键字
用于修饰成员,包括成员属性【类属性或者静态属性】、成员方法【类方法或者静态方法】
- 随着类加载,随着类消失
- 优先于对象,用类名直接访问
public class A {
public static int kk=123;//静态属性
public static void pp() {}//静态方法
public static void main(String[] args) {
//针对静态属性,即可以直接以类名的方式进行调用,也可以创建对象后当作普通属性进行调用
System.out.println(A.kk);//采用类名称直接调用
A.pp();
//采用类名称直接调用
A a=new A();
System.out.println(a.kk); //采用对象名的方式进行调用
a.pp();//采用对象名的方式进行调用
}
}
静态属性
static属性是当前类的所有对象所共有的共同属性(只有一个,而普通属性各个对象都有自己的,相互隔离),任何一个当前类对象修改这个属性,所有其他类对象的这个属性都会受影响
public class A {
public A() {
System.out.println("A....");
}
private B b=new B();
public static void main(String[] args) {
new A();
}
private static C c=new C();
}
class C{
public C() {
System.out.println("C....");
}
}
class B{
public B() {
System.out.println("B....");
}
}
执行过程:
-
静态属性,在类加载完毕后则自动进行静态资源处理
**A a;这里并没有创建A对象,但是C自动创建了,执行了static C c=new C(); -
如果是调用new操作
**非静态属性
**构造器
典型应用:统计A1类的构建次数
public class A {
private static int counter=0;
public A() {
this.counter++; //不考虑多线程并发问题
}
public int getCounter() {
return counter;
}
}
执行顺序: 静态属性—属性—构造器
public class A extends AParent{
public A() {
System.out.println("A....");
}
private B b=new B();
public static void main(String[] args) {
A a=new A();
}
private static C c=new C();
}
class AParent{
private static D d=new D();
private E e=new E();
public AParent() {
System.out.println("A parent....");
}
}
class C{
public C() {
System.out.println("C....");
}
}
class D{
public D() {
System.out.println("D....");
}
}
class E{
public E() {
System.out.println("E....");
}
}
class B{
public B() {
System.out.println("B....");
}
}
访问的方式
可以使用“类名.静态属性名”或者“对象名.静态属性名”的方式进行访问。【范围限制】
定义常量
命名规则:名称全大写,下划线分词
- 声明语法:public static nal double **MIN_NUMBER = 0.1;
- 声明的同时直接进行初始化
**public static nal double MIN_NUMBER = 0.1; - 先声明后在static静态块中赋值
public static final double MIN_NUMBER;
static{
MIN_NUMBER = 0.1;
}
静态方法
——因为可以直接使用”类名.方法名”的形式直接调用静态方法,静态方法执行时很有可能并没有构建对象,所以在静态方法中不允许使用this/super之类用于指定对象的关键字
public class B{
private Random random;
public static void pp() {
System.out.println("pp....");
//静态方法的调用没有要求必须构建B对象,所以很有可能random根本不存在,要求在静态方法中不能访问非静态成员
//
System.out.println(random);
// 在static方法中不允许出现this或者super关键字
}
public static void main(String[] args) {
B.pp();//调用当前类中的static方法,可以直接写方法名称pp()
- 当然在静态方法中允许创建对象,并调用对象方法
- 静态方法只能直接访问静态成员,不能直接访问非静态成员
静态块
public class A{
static { //类内,所有方法之外
System.out.println("静态代码块");
}
}
- 类在执行时需要通过一个叫作类加载器的组件将程序加载到内存中,类在运行时一般不会发生变化,所以类不会频繁加载,在整个运行过程中只加载一次,而且常驻内存
- 静态块在类加载完毕后自动执行,而且只执行一次
public class B{
static {
System.out.println("static...");
}
public static void main(String[] args) {
}
}
非静态块
非静态块在类内且在所有的方法之外,非静态块并不会在类加载后自动执行,而是在构建当前对象时自动执行。
new一次则会执行一次,执行时机在构造器之前执行
- 当类加载完毕会自动优先处理static属性和static块,这两个优先级是相同的,所以谁在前先处理谁
- new对象时,处理非静态属性和非静态块,这两个优先级是相同的,所以谁在前先处理谁
- 最后执行构造器
使用注意事项
静态方法只能访问静态成员,静态有访问局限性
静态方法中不能有this super关键字
主函数是静态的
什么时候使用静态
- 当成员变量的数据各个对象都相同时,可以用static修饰的,让多个对象共享
- 方法如果访问了特有数据(非静态成员变量),该函数是非静态的。方法如果没有访问特有数据,那么该方法就是静态修饰
- 如果类中的功能都是静态的,那么该类创建对象是没有意义的,所以构造方法需要私有化
方法中的可变长个数的参数
语法:数据类型… 变量名。-必须作为最后一个参数出现
具体处理过程中实际上是按照数组的方式进行处理,而且数组不会为null
public void pp(int... k1){
if(k1.length>0){
for(int i=0;i<k1.length;i++)
System.out.println(k1[i]);
}
}
static相关问题
父类中定义的静态方法(类方法)是否可以通过”子类名.静态方法名”的方式进行调用?
可以,因为继承的特性
public class Test1 {
public static void main(String[] args) {
Son.pp();
}
}
class Fa{
public static void pp(){
System.out.println("Fa.pp()");
}
}
class Son extends Fa{}
父类中定义的静态方法(类方法)是否可以在子类中进行覆盖定义(重写)?
可以,但是方法签名必须一致,方法必须为static定义
public class Test1 {
public static void main(String[] args) {
Son.pp();
}
}
class Fa{
public static void pp(){
System.out.println("Fa.pp()");
}
}
class Son extends Fa{
public static void pp(){
System.out.println("Son.pp()");
}
}
特殊点:静态方法也允许通过对象进行调用
public class Test1 {
public static void main(String[] args) {
Fa ff=new Son();
ff.pp();
//注意这里调用的是父类中定义的静态方法,不是子类中覆盖定义的新方法
}
}
class Fa{
public static void pp(){
System.out.println("Fa.pp()");
}
}
class Son extends Fa{
public static void pp(){
System.out.println("Son.pp()");
}
}
静态块没有覆盖定义的概念,如果父子类中都定义了静态块,一定是先执行父类中的静态块,然后运行子类中的静
态块
- 加载顺序为先父后子—先执行父类中定义的静态属性和静态块
- 然后执行初始化操作—先处理父类中的成员属性和非静态块,再执行父类构造器;后执行子类的定义内容
静态导入
在一个类中反复使用到某个类中的静态方法,如果使用静态导入则在当前类中不需要再写类名称
double a1=-12.345;
System.out.println(Math.abs(a1));
System.out.println(Math.cos(a1));
System.out.println(Math.sin(a1));
在JDK5当中提供了导入静态方法的import语句。
- 语法:import static java.lang.Math.*; - 导入Math类中的所有静态方法
- 注意要求使用JDK1.5+版本,否则编译不通过
import static java.lang.Math.*;
public class Test1 {
public static void main(String[] args) {
double a1 = -12.345;
System.out.println(abs(a1));
System.out.println(cos(a1));
System.out.println(sin(a1));
}
}