当我们编写一个类时,其实就是在描述其对象的属性和行为,而并没有产生实质上 的对象,只有通过new关键字才会产生出对象,这时系统才会分配内存空间给对象, 其方法才可以供外部调用。我们有时候希望无论是否产生了对象或无论产生了多少 对象的情况下,某些特定的数据在内存空间里只有一份,例如所有的中国人都有个 国家名称,每一个中国人都共享这个国家名称,不必在每一个中国人的实例对象中 都单独分配一个用于代表国家名称的变量。
使用范围: 主要用来修饰类的内部结构 在Java类中,可用static修饰属性、方法、代码块、内部类被修饰后的成员具备以下特点: 随着类的加载而加载优先于对象存在 修饰的成员,被所有对象所共享访问权限允许时,可不创建对象,直接被类调用类变量 vs 实例变量内存解析
类方法(class method)
没有对象的实例时,可以用==类名.方法名()==的形式访问由static修饰的类方法。在static方法内部只能访问类的static修饰的属性或方法,不能访问类的非static的结构。因为不需要实例就可以访问static方法,因此static方法内部不能有this。(也不能有super ? YES!)static 修饰的方法不能被重写饿汉式和懒汉式
私有化构造器。2.一开始就创建一个对象、第一次被使用的时候在创建对象。有点:由于单例模式只生成一个实例,减少了系统性能开销。
/* * 单例设计模式: * 1. 所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例。 * * 2. 如何实现? * 饿汉式 vs 懒汉式 * * 3. 区分饿汉式 和 懒汉式 * 饿汉式: * 坏处:对象加载时间过长。(就是一开始就加载对象, 但是这个对象长时间没有被使用,所以叫加载时间过长) * 好处:饿汉式是线程安全的 * * 懒汉式:好处:延迟对象的创建。(用的多) * 目前的写法坏处:线程不安全。--->到多线程内容时,再修改 * * */ //饿汉式 class Bank{ //1.私有化类的构造器 private Bank(){ } //2.内部创建类的对象 //4.要求此对象也必须声明为静态的 private static Bank instance = new Bank(); //3.提供公共的静态的方法,返回类的对象 public static Bank getInstance(){ return instance; } } // 懒汉式 class Order{ //1.私有化类的构造器 private Order(){ } //2.声明当前类对象,没有初始化 //4.此对象也必须声明为static的 private static Order instance = null; //3.声明public、static的返回当前类对象的方法 public static Order getInstance(){ if(instance == null){ instance = new Order(); } return instance; } }理解main方法的语法
由于Java虚拟机需要调用类的main()方法,所以该方法的访问权限必须是 public,又因为Java虚拟机在执行main()方法时不必创建对象,所以该方法必须 是static的,该方法接收一个String类型的数组参数,该数组中保存执行Java命令时传递给所运行的类的参数。
又因为main() 方法是静态的,我们不能直接访问该类中的非静态成员,必须创 建该类的一个实例对象后,才能通过这个对象去访问类中的非静态成员,这种情况,我们在之前的例子中多次碰到。
main()方法的使用说明
main() 方法作为程序的入口。main() 方法也是一个普通的静态方法mian() 方法可以作为我们与控制台交互的方式。(之前:使用Scanner(System.in) $ java Demo args1 args2 # program arguments,多个程序参数用空格隔开代码块(或初始化块)的作用:
对Java类或对象进行初始化
代码块(或初始化块)的分类:
一个类中代码块若有修饰符 , 则只能被 static 修饰 , 称为静态代码块 (static block),没有使用static修饰的,为非静态代码块。static代码块通常用于初始化static的属性
class Person { public static int total; static { total = 100;//为total赋初值 } }静态代码块:用static 修饰的代码块
可以有输出语句。
可以对类的属性、类的声明进行初始化操作。
不可以对非静态的属性初始化。即:不可以调用非静态的属性和方法。
若有多个静态的代码块,那么按照从上到下的顺序依次执行。
静态代码块的执行要先于非静态代码块。
静态代码块随着类的加载而加载,且只执行一次。
非静态代码块:没有static修饰的代码块 可以有输出语句。可以对类的属性、类的声明进行初始化操作。除了调用非静态的结构外,还可以调用静态的变量或方法。若有多个非静态的代码块,那么按照从上到下的顺序依次执行。每次创建对象的时候,都会执行一次。且先于构造器执行。程序中成员变量赋值的执行顺序
在Java中声明类、变量和方法时,可使用关键字final来修饰,表示“最终的”。
final标记的类不能被继承。提高安全性,提高程序的可读性。
比如:String类、System类、StringBuffer类
final标记的方法不能被子类重写。比如:Object类中的getClass()。
final标记的变量(成员变量或局部变量)即称为常量。名称大写,且只能被赋值一次。(名称大写只是一种规范)
final标记的成员变量必须在声明时或在每个构造器中或代码块中显式赋 值,然后才能使用。final double MY_PI = 3.14;static final 修饰的属性叫做全局常量
static 可以修饰属性、方法、代码块、内部类final 可以修饰类、属性、变量、方法。所以static final 可修饰变量、方法概述
随着继承层次中一个个新子类的定义,类变得越来越具体,而父类则更一 般,更通用。类的设计应该保证父类和子类能够共享特征。有时将一个父 类设计得非常抽象,以至于它没有具体的实例,这样的类叫做抽象类
抽象类与抽象方法
用abstract关键字来修饰一个类,这个类叫做抽象类。
用abstract来修饰一个方法,该方法叫做抽象方法。
抽象方法:只有方法的声明,没有方法的实现。以分号结束: 比如:public abstract void talk();含有抽象方法的类必须被声明为抽象类。
抽象类不能被实例化。抽象类是用来被继承的,抽象类的子类必须重 写父类的抽象方法,并提供方法体。若没有重写全部的抽象方法,仍 为抽象类。
不能用abstract修饰变量、代码块、构造器;
不能用abstract修饰私有方法、静态方法、final的方法、final的类。
/* * abstract关键字的使用 * 1.abstract:抽象的 * 2.abstract可以用来修饰的结构:类、方法 * * 3. abstract修饰类:抽象类 * > 此类不能实例化 * > 抽象类中一定有构造器,便于子类实例化时调用(涉及:子类对象实例化的全过程) * > 开发中,都会提供抽象类的子类,让子类对象实例化,完成相关的操作 * * * 4. abstract修饰方法:抽象方法 * > 抽象方法只有方法的声明,没有方法体 * > 包含抽象方法的类,一定是一个抽象类。反之,抽象类中可以没有抽象方法的。 * > 若子类重写了父类中的所有的抽象方法后,此子类方可实例化 * 若子类没有重写父类中的所有的抽象方法,则此子类也是一个抽象类,需要使用abstract修饰 */抽象类的匿名子类
public class Demo{ public static void main(String[] args){ new Person(){ @Override public void eat(){ } } } } abstract class Person{ //抽象方法 public abstract void eat(); }模板方法设计模式
应用场景:
数据库访问的封装 Junit单元测试 JavaWeb的Servlet中关于doGet/doPost方法调用 Hibernate中模板程序 Spring中JDBCTemlate、HibernateTemplate等钩子函数、回调函数。
package com.atguigu.java; /* * 抽象类的应用:模板方法的设计模式 * */ public class TemplateTest { public static void main(String[] args) { SubTemplate t = new SubTemplate(); t.spendTime(); } } abstract class Template{ //计算某段代码执行所需要花费的时间 public void spendTime(){ long start = System.currentTimeMillis(); this.code();//不确定的部分、易变的部分 long end = System.currentTimeMillis(); System.out.println("花费的时间为:" + (end - start)); } public abstract void code(); } class SubTemplate extends Template{ @Override public void code() { for(int i = 2;i <= 1000;i++){ boolean isFlag = true; for(int j = 2;j <= Math.sqrt(i);j++){ if(i % j == 0){ isFlag = false; break; } } if(isFlag){ System.out.println(i); } } } } package com.atguigu.java; //抽象类的应用:模板方法的设计模式 public class TemplateMethodTest { public static void main(String[] args) { BankTemplateMethod btm = new DrawMoney(); btm.process(); BankTemplateMethod btm2 = new ManageMoney(); btm2.process(); } } abstract class BankTemplateMethod { // 具体方法 public void takeNumber() { System.out.println("取号排队"); } public abstract void transact(); // 办理具体的业务 //钩子方法 public void evaluate() { System.out.println("反馈评分"); } // 模板方法,把基本操作组合到一起,子类一般不能重写 public final void process() { this.takeNumber(); this.transact();// 像个钩子,具体执行时,挂哪个子类,就执行哪个子类的实现代码 this.evaluate(); } } class DrawMoney extends BankTemplateMethod { public void transact() { System.out.println("我要取款!!!"); } } class ManageMoney extends BankTemplateMethod { public void transact() { System.out.println("我要理财!我这里有2000万美元!!"); } }接口就是规范,定义的是一组规则,体现了现实世界中“如果你是/要…则 必须能…”的思想。继承是一个"是不是"的关系,而接口实现则是 “能不能” 的关系。
接口的本质是契约,标准,规范,就像我们的法律一样。制定好后大家都 要遵守。
类是单继承、接口是多继承。
class A extends Object implements InterfaceA,InterfaceB{ }接口(interface)是抽象方法和常量值定义的集合。
接口的特点:
用interface来定义。接口中的所有成员变量都默认是由public static final修饰的。接口中的所有抽象方法都默认是由public abstract修饰的。接口中没有构造器。 接口采用多继承机制。(抽象类是有无参构造器的,只是我们调用不了而已)定义Java类的语法格式:先写extends,后写implements
class SubClass extends SuperClass implements InterfaceA{ }一个类可以实现多个接口,接口也可以继承其它接口。
实现接口的类中必须提供接口中所有方法的具体实现内容,方可实 例化。否则,仍为抽象类。
接口的主要用途就是被实现类实现。(面向接口编程)
与继承关系类似,接口与实现类之间存在多态性
接口和类是并列关系,或者可以理解为一种特殊的类。从本质上讲, 接口是一种特殊的抽象类,这种抽象类中只包含常量和方法的定义 (JDK7.0及之前),而没有变量和方法的实现。
/* * 接口的使用 * 1.接口使用interface来定义 * 2.Java中,接口和类是并列的两个结构 * 3.如何定义接口:定义接口中的成员 * * 3.1 JDK7及以前:只能定义全局常量和抽象方法 * >全局常量:public static final的.但是书写时,可以省略不写 * >抽象方法:public abstract的 * * 3.2 JDK8:除了定义全局常量和抽象方法之外,还可以定义静态方法、默认方法(略) * * 4. 接口中不能定义构造器的!意味着接口不可以实例化 * * 5. Java开发中,接口通过让类去实现(implements)的方式来使用. * 如果实现类覆盖了接口中的所有抽象方法,则此实现类就可以实例化 * 如果实现类没有覆盖接口中所有的抽象方法,则此实现类仍为一个抽象类 * * 6. Java类可以实现多个接口 --->弥补了Java单继承性的局限性 * 格式:class AA extends BB implements CC,DD,EE * * 7. 接口与接口之间可以继承,而且可以多继承 * * ******************************* * 8. 接口的具体使用,体现多态性。因为接口不能实例化,必须要有子类才能实现。 * 9. 接口,实际上可以看做是一种规范 * * 面试题:抽象类与接口有哪些异同? * */Java 8中,你可以为接口添加静态方法和默认方法。从技术角度来说,这是完 全合法的,只是它看起来违反了接口作为一个抽象定义的理念。
静态方法:使用 static 关键字修饰。可以通过接口直接调用静态方法,并执行 其方法体。我们经常在相互一起使用的类中使用静态方法。你可以在标准库中 找到像Collection/Collections或者Path/Paths这样成对的接口和类。
默认方法:默认方法使用 default 关键字修饰。可以通过实现类对象来调用。 我们在已有的接口中提供新方法的同时,还保持了与旧版本代码的兼容性。 比如:java 8 API中对Collection、List、Comparator等接口提供了丰富的默认 方法。
若一个接口中定义了一个默认方法,而另外一个接口中也定义了一个同名同 参数的方法(不管此方法是否是默认方法),在实现类同时实现了这两个接 口时,会出现:接口冲突。
解决办法:实现类必须覆盖接口中同名同参数的方法,来解决冲突。
若一个接口中定义了一个默认方法,而父类中也定义了一个同名同参数的非 抽象方法,则不会出现冲突问题。因为此时遵守:类优先原则。接口中具有 相同名称和参数的默认方法会被忽略。
/* * * JDK8:除了定义全局常量和抽象方法之外,还可以定义静态方法、默认方法 * */ public interface CompareA { //静态方法 public static void method1(){ System.out.println("CompareA:北京"); } //默认方法 public default void method2(){ System.out.println("CompareA:上海"); } default void method3(){ System.out.println("CompareA:上海"); } } package com.atguigu.java8; public class SubClassTest { public static void main(String[] args) { SubClass s = new SubClass(); // s.method1(); // SubClass.method1(); //知识点1:接口中定义的静态方法,只能通过接口来调用。 CompareA.method1(); //知识点2:通过实现类的对象,可以调用接口中的默认方法。 //如果实现类重写了接口中的默认方法,调用时,仍然调用的是重写以后的方法 s.method2(); //知识点3:如果子类(或实现类)继承的父类和实现的接口中声明了同名同参数的默认方法, //那么子类在没有重写此方法的情况下,默认调用的是父类中的同名同参数的方法。-->类优先原则 //知识点4:如果实现类实现了多个接口,而这多个接口中定义了同名同参数的默认方法, //那么在实现类没有重写此方法的情况下,报错。-->接口冲突。 //这就需要我们必须在实现类中重写此方法 s.method3(); } } class SubClass extends SuperClass implements CompareA,CompareB{ public void method2(){ System.out.println("SubClass:上海"); } public void method3(){ System.out.println("SubClass:深圳"); } //知识点5:如何在子类(或实现类)的方法中调用父类、接口中被重写的方法 public void myMethod(){ method3();//调用自己定义的重写的方法 super.method3();//调用的是父类中声明的 //调用接口中的默认方法 CompareA.super.method3(); CompareB.super.method3(); } }抽象类与接口有哪些异同?
都不能实例化分类:
成员内部类(static成员内部类和非static成员内部类)局部内部类(不谈修饰符)、匿名内部类相同点:不能实例化,都可以被继承
不同点:抽象类:有构造器。接口:不能声明构造器
多继承 vs 单继承
随着jdk不断的迭代,接口越来越像类了。
局部内部类,使用局部变量,变量必须声明为final类型(jdk默认会帮我们填写final)
/* * 在局部内部类的方法中(比如:show)如果调用局部内部类所声明的方法(比如:method)中的局部变量(比如:num)的话, * 要求此局部变量声明为final的。 * * jdk 7及之前版本:要求此局部变量显式的声明为final的 * jdk 8及之后的版本:可以省略final的声明 * */ public void method(){ //局部变量 int num = 10; class AA{ public void show(){ // num = 20; System.out.println(num); } } }