关键字:static
当我们编写一个类时,其实就是在描述其对象的属性和行为,而并没有产生实质上 的对象,只有通过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方法的语法
理解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修饰的代码块
- 可以有输出语句。
- 可以对类的属性、类的声明进行初始化操作。
- 除了调用非静态的结构外,还可以调用静态的变量或方法。
- 若有多个非静态的代码块,那么按照从上到下的顺序依次执行。
- 每次创建对象的时候,都会执行一次。且先于构造器执行。
程序中成员变量赋值的执行顺序
关键字:final
-
在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万美元!!");
}
}
接口(interface)
概述
接口就是规范,定义的是一组规则,体现了现实世界中“如果你是/要…则 必须能…”的思想。继承是一个"是不是"的关系,而接口实现则是 “能不能” 的关系。
接口的本质是契约,标准,规范,就像我们的法律一样。制定好后大家都 要遵守。
类是单继承、接口是多继承。
class A extends Object implements InterfaceA,InterfaceB{
}
JDK7及以前接口特点
-
接口(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. 接口,实际上可以看做是一种规范
*
* 面试题:抽象类与接口有哪些异同?
*
*/
Jdk8 接口的特点
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();
}
}
抽象类与接口有哪些异同?
- 都不能实例化
接口的匿名实现
package com.atguigu.java1;
/*
* 接口的使用
* 1.接口使用上也满足多态性
* 2.接口,实际上就是定义了一种规范
* 3.开发中,体会面向接口编程!
*
*/
public class USBTest {
public static void main(String[] args) {
//3. 创建了接口的匿名实现类的非匿名对象
USB phone = new USB(){
@Override
public void start() {
System.out.println("手机开始工作");
}
@Override
public void stop() {
System.out.println("手机结束工作");
}
};
com.transferData(phone);
//4. 创建了接口的匿名实现类的匿名对象
com.transferData(new USB(){
@Override
public void start() {
System.out.println("mp3开始工作");
}
@Override
public void stop() {
System.out.println("mp3结束工作");
}
});
}
}
interface USB{
//常量:定义了长、宽、最大最小的传输速度等
void start();
void stop();
}
接口的应用
代理模式
package com.atguigu.java1;
/*
* 接口的应用:代理模式
*
*/
public class NetWorkTest {
public static void main(String[] args) {
Server server = new Server();
// server.browse();
ProxyServer proxyServer = new ProxyServer(server);
proxyServer.browse(); // 就是我们调用的是proxyServer的browse方法但是其实是调用server的方法
}
}
interface NetWork{
public void browse();
}
//被代理类
class Server implements NetWork{
@Override
public void browse() {
System.out.println("真实的服务器访问网络");
}
}
//代理类
class ProxyServer implements NetWork{
private NetWork work;
public ProxyServer(NetWork work){
this.work = work;
}
public void check(){
System.out.println("联网之前的检查工作");
}
@Override
public void browse() {
check();
work.browse();
}
}
package com.atguigu.java1;
public class StaticProxyTest {
public static void main(String[] args) {
Proxy s = new Proxy(new RealStar());
s.confer();
s.signContract();
s.bookTicket();
s.sing();
s.collectMoney();
}
}
interface Star {
void confer();// 面谈
void signContract();// 签合同
void bookTicket();// 订票
void sing();// 唱歌
void collectMoney();// 收钱
}
//被代理类
class RealStar implements Star {
public void confer() {
}
public void signContract() {
}
public void bookTicket() {
}
public void sing() {
System.out.println("明星:歌唱~~~");
}
public void collectMoney() {
}
}
//代理类
class Proxy implements Star {
private Star real;
public Proxy(Star real) {
this.real = real;
}
public void confer() {
System.out.println("经纪人面谈");
}
public void signContract() {
System.out.println("经纪人签合同");
}
public void bookTicket() {
System.out.println("经纪人订票");
}
public void sing() {
real.sing();
}
public void collectMoney() {
System.out.println("经纪人收钱");
}
}
类的成员之五:内部类
概述
分类:
- 成员内部类(static成员内部类和非static成员内部类)
- 局部内部类(不谈修饰符)、匿名内部类
应用
package com.atguigu.java2;
/*
* 类的内部成员之五:内部类
* 1. Java中允许将一个类A声明在另一个类B中,则类A就是内部类,类B称为外部类
*
* 2.内部类的分类:成员内部类(静态、非静态) vs 局部内部类(方法内、代码块内、构造器内)
*
* 3.成员内部类:
* 一方面,作为外部类的成员:
* >调用外部类的结构
* >可以被static修饰
* >可以被4种不同的权限修饰
*
* 另一方面,作为一个类:
* > 类内可以定义属性、方法、构造器等
* > 可以被final修饰,表示此类不能被继承。言外之意,不使用final,就可以被继承
* > 可以被abstract修饰
*
*
* 4.关注如下的3个问题
* 4.1 如何实例化成员内部类的对象
* 4.2 如何在成员内部类中区分调用外部类的结构
* 4.3 开发中局部内部类的使用 见《InnerClassTest1.java》
*
*/
public class InnerClassTest {
public static void main(String[] args) {
//创建Dog实例(静态的成员内部类):
Person.Dog dog = new Person.Dog();
dog.show();
//创建Bird实例(非静态的成员内部类):
// Person.Bird bird = new Person.Bird();//错误的
Person p = new Person();
Person.Bird bird = p.new Bird();
bird.sing();
System.out.println();
bird.display("黄鹂");
}
}
class Person{
String name = "小明";
int age;
public void eat(){
System.out.println("人:吃饭");
}
//静态成员内部类
static class Dog{
String name;
int age;
public void show(){
System.out.println("卡拉是条狗");
// eat();
}
}
//非静态成员内部类
class Bird{
String name = "杜鹃";
public Bird(){
}
public void sing(){
System.out.println("我是一只小小鸟");
Person.this.eat();//调用外部类的非静态属性
eat();
System.out.println(age);
}
public void display(String name){
System.out.println(name);//方法的形参
System.out.println(this.name);//内部类的属性
System.out.println(Person.this.name);//外部类的属性
}
}
public void method(){
//局部内部类
class AA{
}
}
{
//局部内部类
class BB{
}
}
public Person(){
//局部内部类
class CC{
}
}
}
package com.atguigu.java2;
public class InnerClassTest1 {
//开发中很少见
public void method(){
//局部内部类
class AA{
}
}
//返回一个实现了Comparable接口的类的对象
public Comparable getComparable(){
//创建一个实现了Comparable接口的类:局部内部类
//方式一:
// class MyComparable implements Comparable{
//
// @Override
// public int compareTo(Object o) {
// return 0;
// }
//
// }
//
// return new MyComparable();
//方式二:
return new Comparable(){
@Override
public int compareTo(Object o) {
return 0;
}
};
}
}
总结
- 成员内部类和局部内部类,在编译以后,都会生成字节码文件。
- 格式:
- 成员内部类:外部类$内部类名.class
- 局部内部类:外部类$数字 内部类名.class
接口和抽象类的共同点和区别
相同点:不能实例化,都可以被继承
不同点:抽象类:有构造器。接口:不能声明构造器
多继承 vs 单继承
随着jdk不断的迭代,接口越来越像类了。
练习题
package com.atguigu.java3;
//总结:由父及子,静态先行
class Root{
static{
System.out.println("Root的静态初始化块");
}
{
System.out.println("Root的普通初始化块");
}
public Root(){
super();
System.out.println("Root的无参数的构造器");
}
}
class Mid extends Root{
static{
System.out.println("Mid的静态初始化块");
}
{
System.out.println("Mid的普通初始化块");
}
public Mid(){
super();
System.out.println("Mid的无参数的构造器");
}
public Mid(String msg){
//通过this调用同一类中重载的构造器
this();
System.out.println("Mid的带参数构造器,其参数值:"
+ msg);
}
}
class Leaf extends Mid{
static{
System.out.println("Leaf的静态初始化块");
}
{
System.out.println("Leaf的普通初始化块");
}
public Leaf(){
//通过super调用父类中有一个字符串参数的构造器
super("尚硅谷");
System.out.println("Leaf的构造器");
}
}
public class LeafTest{
public static void main(String[] args){
new Leaf();
System.out.println();
new Leaf();
}
}
package com.atguigu.java3;
class Father {
static {
System.out.println("11111111111");
}
{
System.out.println("22222222222");
}
public Father() {
System.out.println("33333333333");
}
}
public class Son extends Father {
static {
System.out.println("44444444444");
}
{
System.out.println("55555555555");
}
public Son() {
System.out.println("66666666666");
}
public static void main(String[] args) { // 由父及子 静态先行
System.out.println("77777777777");
System.out.println("************************");
new Son();
System.out.println("************************");
new Son();
System.out.println("************************");
new Father();
}
}
局部内部类,使用局部变量,变量必须声明为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);
}
}
}