峨眉山月半轮秋,影入平羌江水流。—-李白《峨眉山月歌》
static 的作用
public修饰的static成员变量和成员方法本质是全局变量和全局方法,当声明它类的对象市,不生成static变量的副本,而是类的所有实例共享同一个static变量。
static变量前可以有private修饰,表示这个变量可以在类的静态代码块中,或者类的其他静态成员方法中使用(当然也可以在非静态成员方法中使用),但是不能在其他类中通过类名来直接引用,这一点很重要。实际上你需要搞明白,private是访问权限限定,static表示不要实例化就可以使用。
类名.静态方法名(参数列表…)
类名.静态变量名
static变量
JVM只为静态分配一次内存,在加载类的过程中完成静态变量的内存分配,可用类名直接访问
static方法
静态方法可以直接通过类名调用,任何的实例也都可以调用
因此静态方法中不能用this和super关键字,不能直接访问所属类的实例变量和实例方法(就是不带static的成员变量和成员成员方法),只能访问所属类的静态成员变量和成员方法。
因为实例成员与特定的对象关联!
因为static方法独立于任何实例,因此static方法必须被实现,而不能是抽象的abstract。
仅能调用其他的static 方法。
只能访问static数据
static代码块
static代码块也叫静态代码块,是在类中独立于类成员的static语句块,可以有多个,位置可以随便放,它不在任何的方法体内,JVM加载类时会执行这些静态的代码块,如果static代码块有多个,JVM将按照它们在类中出现的先后顺序依次执行它们,每个代码块只会被执行一次
//示例1
public class Test5 {
private static int a;
private int b;
static {
Test5.a = 3;
System.out.println(a);
Test5 t = new Test5();
t.f();
t.b = 1000;
System.out.println(t.b);
}
static {
Test5.a = 4;
System.out.println(a);
}
public static void main(String[] args) {
}
static {
Test5.a = 5;
System.out.println(a);
}
public void f() {
System.out.println("hhahhahah");
}
}
//结果
3
hhahhahah
1000
4
5
//示例2
class UseStatic {
static int a = 3;
static int b;
static void meth(int x) {
System.out.println("x = " + x);
System.out.println("a = " + a);
System.out.println("b = " + b);
}
static {
System.out.println("Static block initialized.");
b = a * 4;
}
public static void main(String args[]) {
meth(42);
}
}
/*一旦UseStatic 类被装载,所有的static语句被运行。首先,a被设置为3,接着static 块执行(打印一条消息),最后,b被初始化为a*4 或12。然后调用main(),main() 调用meth() ,把值42传递给x。3个println ( ) 语句引用两个static变量a和b,以及局部变量x */
结果:
Static block initialized.
x = 42
a = 3
b = 12
在一个static 方法中引用任何实例变量都是非法的
//错误
static int vode = 0;
int count;
static void getAll{
count=vode;
System.out.print(count);
}
/*static 变量在加载类就在内存中,而count只有在类调用时才存在,
即表示count还没存在内存中,然后再static方法中用到了
这个count,一个不存在的变量Java肯定报错了*/
static和final一块用
static final用来修饰成员变量和成员方法,可简单理解为“全局常量”! 对于变量,表示一旦给值就不可修改,并且通过类名可以访问。
对于方法,表示不可覆盖,并且可以通过类名直接访问
this的作用
this总是指向调用该方法的对象
ThisDemo.java
public class ThisDemo {
String name="Mick";
public void print(String name){
System.out.println("类中的属性 name="+this.name);
System.out.println("局部传参的属性="+name);
}
public static void main(String[] args) {
ThisDemo tt=new ThisDemo();
tt.print("Orson");
}
}
ThisDeme.java
public class ThisDemo {
int number;
ThisDemo increment(){
number++;
return this;
}
private void print(){
System.out.println("number="+number);
}
public static void main(String[] args) {
ThisDemo tt=new ThisDemo();
tt.increment().increment().increment().print();
}
}
结果:
number=3
ThisDemo.java
public class ThisDemo {
String name;
int age;
public ThisDemo (){
this.age=21;
}
public ThisDemo(String name,int age){
this();
this.name="Mick";
}
private void print(){
System.out.println("最终名字="+this.name);
System.out.println("最终的年龄="+this.age);
}
public static void main(String[] args) {
ThisDemo tt=new ThisDemo("",0);
tt.print();
}
}
结果:
最终名字=Mick
最终的年龄=21
示例
public class ThisTest {
private int i=0;
//第一个构造器:有一个int型形参
ThisTest(int i){
this.i=i+1;//此时this表示引用成员变量i,而非函数参数i
System.out.println("Int constructor i——this.i: "+i+"——"+this.i);
System.out.println("i-1:"+(i-1)+" this.i+1:"+(this.i+1));
//从两个输出结果充分证明了i和this.i是不一样的!
}
// 第二个构造器:有一个String型形参
ThisTest(String s){
System.out.println("String constructor: "+s);
}
// 第三个构造器:有一个int型形参和一个String型形参
ThisTest(int i,String s){
this(s);//this调用第二个构造器
//this(i);
/*此处不能用,因为其他任何方法都不能调用构造器,只有构造方法能调用他。
但是必须注意:就算是构造方法调用构造器,也必须为于其第一行,构造方法也只能调
用一个且仅一次构造器!*/
this.i=i++;//this以引用该类的成员变量
System.out.println("Int constructor: "+i+" String constructor: "+s);
}
public ThisTest increment(){
this.i++;
return this;//返回的是当前的对象,该对象属于(ThisTest)
}
public static void main(String[] args){
ThisTest tt0=new ThisTest(10);
ThisTest tt1=new ThisTest("ok");
ThisTest tt2=new ThisTest(20,"ok again!");
System.out.println(tt0.increment().increment().increment().i);
//tt0.increment()返回一个在tt0基础上i++的ThisTest对象,
//接着又返回在上面返回的对象基础上i++的ThisTest对象!
}
}
面向对象高级面向对象基本思想(继承)
面向对象的程序设计语言必须有描述对象及其相互之间关系的语言成分。这些程序设计语言可以归纳为以下几类:系统中一切事物皆为对象;对象是属性及其操作的封装体;对象可按其性质划分为类,对象成为类的实例;实例关系和继承关系是对象之间的静态关系;消息传递是对象之间动态联系的唯一形式,也是计算的唯一形式;方法是消息的序列。
继承的作用
继承是面向对象最显著的一个特性。继承是从已有的类中派生出新的类,新的类能吸收已有类的数据属性和行为,并能扩展新的能力。
Java继承是使用已存在的类的定义作为基础建立新类的技术,新类的定义可以增加新的数据或新的功能,也可以用父类的功能,但不能选择性地继承父类
这种技术使得复用以前的代码非常容易,能够大大缩短开发周期,降低开发费用。比如可以先定义一个类叫车,车有以下属性:车体大小,颜色,方向盘,轮胎,而又由车这个类派生出轿车和卡车两个类,为轿车添加一个小后备箱,而为卡车添加一个大货箱。
其中父类又叫超类或基类,子类又叫派生类。
JAVA不支持多继承,单继承使JAVA的继承关系很简单,一个类只能有一个父类,易于管理程序,同时一个类可以实现多个接口,从而克服单继承的缺点。
public class Test {
public static void main(String[] args) {
new Circle();
}
}
class Draw {
public Draw(String type) {
System.out.println(type+" draw constructor");
}
}
class Shape {
private Draw draw = new Draw("shape");
public Shape(){
System.out.println("shape constructor");
}
}
class Circle extends Shape {
private Draw draw = new Draw("circle");
public Circle() {
System.out.println("circle constructor");
}
}
//父类的构造器调用以及初始化过程一定在子类的前面
结果:
shape draw constructor
shape constructor
circle draw constructor
circle constructor
继承的语法特点
(1)继承关系是传递的。若类C继承类B,类B继承类A(多继承),则类C既有从类B那里继承下来的属性与方法,也有从类A那里继承下来的属性与方法,还可以有自己新定义的属性和方法。继承来的属性和方法尽管是隐式的,但仍是类C的属性和方法。继承是在一些比较一般的类的基础上构造、建立和扩充新类的最有效的手段。
(2)继承简化了人们对事物的认识和描述,能清晰体现相关类间的层次结构关系。
(3)继承提供了软件复用功能。若类B继承类A,那么建立类B时只需要再描述与基类(类A)不同的少量特征(数据成员和成员方法)即可。这种做法能减小代码和数据的冗余度,大大增加程序的重用性。
(4)继承通过增强一致性来减少模块间的接口和界面,大大增加了程序的易维护性。
(5)提供多重继承机制。从理论上说,一个类可以是多个一般类的特殊类,它可以从多个一般类中继承属性与方法,这便是多重继承。Java出于安全性和可靠性的考虑,仅支持单重继承,而通过使用接口机制来实现多重继承。
子类重写父类的方法时,遵循“两同两小一大”,方法名,行参列表相同;子类返回类型更小或相等,抛出更小或相等;访问权限应比父类方法更大或相等
父类的private方法,不可继承,子类写同名的方法,不算重写
super的使用方法
子类方法中调用父类被重写的实例方法,实例变量
super.方法
super.变量
super调用构造方法
Creature.java
class Creature {
public Creature() {
System.out.println("Creature无参数的构造器");
}
}
class Animal extends Creature {
public Animal(String name) {
System.out.println("Animal 带一个参数的构造器,"+"name为"+name);
}
public Animal(String name,int age) {
this(name);
System.out.println("Animal 带两个参数的构造器,"+"age为"+age);
}
}
public class Wolf extends Animal {
public Wolf() {
super("灰太狼",3);
System.out.println("Wolf 无参数的构造器");
}
public static void main(String[] args) {
new Wolf();
}
}
/*
Creature无参数的构造器
Animal 带一个参数的构造器,name灰太狼
Animal 带两个参数的构造器,age3
Wolf 无参数的构造器
创建任何对象总是从该类所在继承树的最顶层的构造器开始执行,然后依次向下执行,最后才执行本类的构造器
*/
面向对象基本思想(多态)
http://blog.csdn.net/peng_hong_fu/article/details/52424008
对象转型
- 一个父类的引用类型变量可以指向其子类的对象。
- 一个父类的引用不可以访问子类对象新增加的成员(属性和方法)
- 可以使用 变量 instanceof 类名 来判断 对象是否属于该类或该类的子类。
- 子类的对象可以当作父类的对象 —向上转型(upcasting),反之称为向下转型(downcasting)
向上转型:子类对象转换为父类引用称为向上转型。此处父类对象可以是接口
或者说父类引用指向子类对象,而子类引用不能指向父类对象
class A {
}
class B extends A {
}
A b = new B();
这个就是向上转型。
为什么要向上转型
因为子类是对父类的一个改进和扩充,所以一般子类在功能上较父类更强大,属性较父类更独特,
定义一个父类类型的引用指向一个子类的对象既可以使用子类强大的功能,又可以抽取父类的共性。
所以,父类类型的引用可以调用父类中定义的所有属性和方法,而对于子类中定义而父类中没有的方法,父类引用是无法调用的;
动态链接
父类的方法没有被子类重写,才可以被父类类型的引用调用; 对于父类中定义的方法,如果子类重写了该方法,那么父类类型的引用将会调用子类中的这个方法。
class Father {
public void func1() {
func2();
}
// 这是父类中的func2()方法,因为下面的子类中重写了该方法
// 所以在父类类型的引用中调用时,这个方法将不再有效
// 取而代之的是将调用子类中重写的func2()方法
public void func2() {
System.out.println("AAA");
}
}
class Child extends Father {
// func1(int i)是对func1()方法的一个重载,主要不是重写!
// 由于在父类中没有定义这个方法,所以它不能被父类类型的引用调用
// 所以在下面的main方法中child.func1(68)是不对的
public void func1(int i) {
System.out.println("BBB");
}
// func2()重写了父类Father中的func2()方法
// 如果父类类型的引用中调用了func2()方法,那么必然是子类中重写的这个方法
public void func2() {
System.out.println("CCC");
}
}
public class PolymorphismTest {
public static void main(String[] args) {
Father child = new Child();//向上转型
child.func1();
child.func1(68);//error
}
}
向下转型:父类引用的对象转换为子类类型称为向下转型。
Child c = (Child)child;
public class Shape {
public static void main(String[] args) {
Triangle tri = new Triangle();
System.out.println("Triangle is a type of shape? " + tri.isShape());// 继承
Shape shape = new Triangle();
System.out.println("My shape has " + shape.getSides() + " sides."); // 多态
Triangle t = (Triangle) shape;//向下轉型
System.out.println("下"+t.getSides()+" "+t.getSides(tri));
Rectangle Rec = new Rectangle();
Shape shape2 = Rec;
System.out.println("下"+t.getSides(Rec));
System.out.println("My shape has " + shape2.getSides(Rec) + " sides."); // 重载
}
public boolean isShape() {
return true;
}
public int getSides() {
return 0;
}
public int getSides(Triangle tri) { // 重载
return 3;
}
public int getSides(Rectangle rec) { // 重载
return 4;
}
}
class Triangle extends Shape {
public int getSides() { // 重写,实现多态
return 3;
}
}
class Rectangle extends Shape {
public int getSides(int i) { // 重载
return i;
}
}
Triangle is a type of shape? true
My shape has 3 sides.
下3 3
下4
My shape has 4 sides.
final关键字的作用
在使用匿名内部类的时候可能会经常用到final关键字
final类
当用final修饰一个类时,表明这个类不能被继承。
final类中的所有成员方法都会被隐式地指定为final方法。
final方法
使用final方法的原因有两个。第一个原因是把方法锁定,以防任何继承类修改它的含义;第二个原因是效率。在早期的Java实现版本中,会将final方法转为内嵌调用。但是如果方法过于庞大,可能看不到内嵌调用带来的任何性能提升。在最近的Java版本中,不需要使用final方法进行这些优化了。
类的private方法会隐式地被指定为final方法。
final变量
如果成员变量或局部变量被修饰为final,那么它就是常量,常量声明是没有默认值,所有声明是必须指定值,且不能改变。
- 成员变量
static变量和实例变量
- 局部变量
抽象类和接口的定义方法
抽象
使用了关键词abstract声明的类叫作“抽象类”。如果一个类里包含了一个或多个抽象方法,类就必须指定成abstract(抽象)。
“抽象方法”,属于一种不完整的方法,只含有一个声明,没有方法主体
抽象类不能用new对象,如果一个非抽象类是某个抽象类的子类,那么必须重写父类的抽象方法,给出方法体,这就是为什么final和abstract不能同时修饰一个方法的原因。
abstract void fun();
[public] abstract class ClassName {
abstract void fun();
}
从这里可以看出,抽象类就是为了继承而存在的,如果你定义了一个抽象类,却不去继承它,那么等于白白创建了这个抽象类,因为你不能用它来做任何事情。对于一个父类,如果它的某个方法在父类中实现出来没有任何意义,必须根据子类的实际需求来进行不同的实现,那么就可以将这个方法声明为abstract方法,此时这个类也就成为abstract类了。
1)抽象方法必须为public或者protected(因为如果为private,则不能被子类继承,子类便无法实现该方法),缺省情况下默认为public。
2)抽象类不能用来创建对象;
3)如果一个类继承于一个抽象类,则子类必须实现父类的抽象方法。如果子类没有实现父类的抽象方法,则必须将子类也定义为为abstract类。
接口
有些类并不需要抽象类的方法,但又不得不重写,接口可以增加类需要的功能,不同的类可以实现相同的接口,同一个类也可以实现多个接口。当一个类不希望通过继承来获得自己具体的某个方法时,就可以考虑实现接口,而不是继承。
接口泛指实体把自己提供给外界的一种抽象化物(可以为另一实体),用以由内部操作分离出外部沟通方法,使其能被修改内部而不影响外界其他实体与其交互的方式。
接口提供了关联以及方法调用上的可插入性,软件系统的规模越大,生命周期越长,接口使得软件系统的灵活性和可扩展性,可插入性方面得到保证。
Java接口是一系列方法的声明,是一些方法特征的集合,一个接口只有方法的特征没有方法的实现,因此这些方法可以在不同的地方被不同的类实现,而这些实现可以具有不同的行为(功能)。
接口把方法的特征和方法的实现分割开来。这种分割体现在接口常常代表一个角色,它包装与该角色相关的操作和属性,而实现这个接口的类便是扮演这个角色的演员。一个角色由不同的演员来演,而不同的演员之间除了扮演一个共同的角色之外,并不要求其它的共同之处。
可插入性
在一个等级结构中的任何一个类都可以实现一个接口,这个接口会影响到此类的所有子类,但不会影响到此类的任何超类。此类将不得不实现这个接口所规定的方法,而其子类可以从此类自动继承这些方法,当然也可以选择置换掉所有的这些方法,或者其中的某一些方法,这时候,这些子类具有了可插入性(并且可以用这个接口类型装载,传递实现了他的所有子类)。
AreaInterface.java
public interface AreaInterface{
double pai=Math.PI;
double area();
}
Circle.java
public class Circle implements AreaInterface{
double r;
public Circle(double x){
r=x;
}
//实现接口中的抽象方法,求圆面积
public double area(){
return pai * r * r;
}
public String toString(){
return "圆:r="+r+"\tarea="+area();
}
}
Rectangle.java
public class Rectangle implements AreaInterface {
double x,y;
public Rectangle(double a,double b) {
x=a;
y=b;
}
//实现接口中的抽象方法,求长方形面积
public double area() {
return x * y;
}
public String toString() {
return "长方形:x="+x+";y="+y+"\t"
area=+area();
}
}
接口和抽象类的语法特征
接口
1 因为java不支持多重继承,所以有了接口,一个类只能继承一个父类,但可以实现多个接口,接口本身也可以继承多个接口。
2 接口里面的成员变量默认都是public static final类型的。必须被显示的初始化。
3 接口里面的方法默认都是public abstract类型的。隐式声明。
4 接口没有构造方法,不能被实例化。
5 接口不能实现另一个接口,但可以继承多个接口。
6 类如果实现了一个接口,那么必须实现接口里面的所有抽象方法,否则类要被定义为抽象类。
抽象类
1 如果将一个类声明为abstract,此类不能生成对象,只能被继承使用。
2 抽象方法必须存在于抽象类中。
3 抽象类中可以有一般的变量和一般的方法。
4 子类继承抽象类必须实现其中抽象方法,除非子类为抽象类。 private void print(){};此语句表示方法的空实现。
abstract void print(); 此语句表示方法的抽象,无实现。
接口和抽象类的区别
1 接口只能包含抽象方法,抽象类可以包含普通方法。
2 接口只能定义静态常量属性,抽象类既可以定义普通属性,也可以定义静态常量属性。
3 接口不包含构造方法,抽象类里可以包含构造方法。
Ab.java
public abstract class Ab {
static final int x = 12;
int i = 11;
void say() {
System.out.println("非抽象方法" + i);
};
abstract void say1();
}
AreaInterface .java
public interface AreaInterface {
double pai = Math.PI;
double area();
}
TestAb.java
public class TestAb extends Ab implements AreaInterface {
@Override
void say1() {
// 抽象方法
System.out.println("抽象方法" + x);
}
public static void main(String args[]) {
TestAb ab = new TestAb();
ab.i = 13;// 抽象类里的非静态变量
System.out.println(Ab.x);// 抽象类的静态方法
ab.say1();
ab.say();// 抽象类里的非抽象方法
System.out.println(ab.area());
}
@Override
public double area() {
// 实现接口
return pai;
}
}
抽象类和接口在面向对象编程当中的地位和意义
抽象
abstract类只关心操作,但不关心操作的具体实现过程,可以使程序设计者把精力放在程序的设计上,而不必拘泥于细节的实现,(将这些细节留给子类的设计者)。
在面向对象方法中,抽象类主要用来进行类型隐藏。构造出一个固定的一组行为的抽象描述,但是这组行为却能够有任意个可能的具体实现方式。这个抽象描述就是抽象类,而这一组任意个可能的具体实现则表现为所有可能的派生类。模块可以操作一个抽象体。由于模块依赖于一个固定的抽象体,因此它可以是不允许修改的;同时,通过从这个抽象体派生,也可扩展此模块的行为功能。为了能够实现面向对象设计的一个最核心的原则OCP(Open-Closed Principle),抽象类是其中的关键所在。
开放封闭原则(OCP,Open Closed Principle)是所有面向对象原则的核心。软件设计本身所追求的目标就是封装变化、降低耦合,而开放封闭原则正是对这一目标的最直接体现。其他的设计原则,很多时候是为实现这一目标服务的,例如以Liskov替换原则实现最佳的、正确的继承层次,就能保证不会违反开放封闭原则。
关于开放封闭原则,其核心的思想是:
软件实体应该是可扩展,而不可修改的。也就是说,对扩展是开放的,而对修改是封闭的。
因此,开放封闭原则主要体现在两个方面:
对扩展开放,意味着有新的需求或变化时,可以对现有代码进行扩展,以适应新的情况。
对修改封闭,意味着类一旦设计完成,就可以独立完成其工作,而不要对类进行任何修改。
接口
接口泛指实体把自己提供给外界的一种抽象化物(可以为另一实体),用以由内部操作分离出外部沟通方法,使其能被修改内部而不影响外界其他实体与其交互的方式。
接口可以降低耦合性,可以让某个模块或功能能够重复利用
参考
《Java程序设计实用教程》