JAVA学习笔记
作者:刘先宝
写作日期:2016.2.23
更新日期:2016.2.25
版本号:2.0
大纲
1.说说JAVA与PHP的变量作用域
2.JAVA继承中的方法的覆盖和重载
3,JAVA类的多态
4,JAVA内部类
1,说说JAVA与PHP的变量作用域
因为JAVA是纯OOP语言,而PHP是基于OOP语言,所以变量的作用域是不同的。
1.JAVA作用域:
在Java中,变量的作用域分为四个级别:类级、对象实例级、方法级、块级。
类级变量又称全局级变量或静态变量,需要使用static关键字修饰。类级变量在类定义后就已经存在,占用内存空间, 可以通过类名来访问,不需要实例化。
对象实例级变量就是成员变量,实例化后才会分配内存空间,才能访问。
方法级变量就是在方法内部定义的变量,就是局部变量。
块级变量就是定义在一个块内部的变量,变量的生存周期就是这个块,出了这个块就消失了,比如if、for 语句的块。 块是指由大括号包围的代码,例如:
{
int age = 3;
String name =
"www.weixueyuan.net";
// 正确,在块内部可以访问 age 和name 变量
System.out.println( name + "已经" + age + "岁了");
}
// 错误,在块外部无法访问 age 和name 变量
System.out.println( name + "已经" + age + "岁了");
说明:
• 方法内部除了能访问方法级的变量,还可以访问类级和实例级的变量。
public class Dog{
static int size=10;
public String str="你好";
public void testTheInner() {
System.out.println(Dog.size);//访问类级
System.out.println(this.str);//访问方法级
}
public static void main(String args[]) {
Dog out= new Dog();
out.testTheInner()
}
}
• 块内部能够访问类级、实例级变量,如果块被包含在方法内部,它还可以访问方法级的变量。
• 方法级和块级的变量必须被显示地初始化,否则不能访问。
public class Demo{
public static String name = "微学苑"; // 类级变量
public int i; // 对象实例级变量
// 属性块,在类初始化属性时候运行
{
int j = 2;// 块级变量
}
public void test1() {
int j = 3; // 方法级变量
if(j == 3) {
int k = 5; // 块级变量
}
// 这里不能访问块级变量,块级变量只能在块内部访问
System.out.println("name=" + name+ ", i=" + i + ", j=" + j);
}
public static void main(String[] args) {
// 不创建对象,直接通过类名访问类级变量
System.out.println(Demo.name);
// 创建对象并访问它的方法
Demo t = new Demo();
t.test1();
}
}
2.PHP作用域 。
PHP 中的每个变量都有一个针对它的作用域,它是指可以在其中访问变量(从而访问它的值)的一个领域。变量的作用域是它们所驻留的页面。
因此, 如果你定义了$var,页面余下部分就可以访问 $var,但是,其它页面一般不能访问它(除非使用特殊的变量session,cookie)。但是,当使用你自己定义的函数时,所有这些都将变得不那么 明显。这些函数具有它们自己的作用域,这意味着在一个函数内使用的变量不能在其外部使用,在一个函数外
部定义的变量不能在其内部使用。
由于这个原因,函数内部的变量可以具有与其外部的变量相同的名称,但是它们仍然是完全不同的变量,并且具有不同的值。要改变一个函数内的变量的作用域。
•可以使用 global 语句和根据每个值作为参数传入。 例如:
$var = 20;
class A {
function __construct() {
//do something
}
function init() {
global $var;
echo $var;
}
}
$A = NEW A();
$A->init();
输出:20;
•常量一旦被声明将可以在全局可见,也就是说,它们可以函数内外使用,但是这仅仅限于一个页面之中(包含我们通过include和include_once)包含进来的PHP脚本,
但是在其他的页面中就不能使用了。例如:
const string = 20;
class A {
function __construct() {
//do something
}
function init() {
echo string;
}
}
$A = NEW A();
$A->init();//输出20;
•在一个脚本中声明的全局变量在整个脚本中是可见的,但不是在函数内部,在函数内部的变量如果与全局变量名称相同,以函数内部的变量为准。
•函数内部使用的变量声明为全局变量时,其名称要与全局变量的名称一致,在这样的情况下,我们就可以在函数中使用函数外部的全局变量了,这样就可以避免上一种因为函数内部的变量与外部的全局变量名称相同而覆盖了外部变量这样的情况。
•在函数内部创建并声明为静态的变量无法在函数外部可见,但是可以在函数的多次执行过程中保持该值,最常见的情况就是在函数的递归执行的过程之中。
function Test(){
static $count = 0;
$count++;
echo $count;
if ($count < 10) {
Test ();
}
$count--;
}
•在函数内部创建的变量对函数来说是本地的,而当函数终止时,该变量也就不存在了。
•PHP中没有块作用域的概念。
$str="str" ;
if(1){
$str="string";
}
echo $str
会输出string,如果有块作用域的话应该输出str.
2、继承中的方法的覆盖和重载
在java中,变量覆盖称为隐藏父类变量,方法称为重载。
方法覆盖的原则:
• 覆盖方法的返回类型、方法名称、参数列表必须与原方法的相同。
• 覆盖方法不能比原方法访问性差(即访问权限不允许缩小)。
• 覆盖方法不能比原方法抛出更多的异常。
• 被覆盖的方法不能是final类型,因为final修饰的方法是无法覆盖的。
• 被覆盖的方法不能为private,否则在其子类中只是新定义了一个方法,并没有对其进行覆盖。
• 被覆盖的方法不能为static。如果父类中的方法为静态的,而子类中的方法不是静态的,但是两个方法除了这一点外其他都满足覆盖条件,那么会发生编译错
误;反之亦然。即使父类和子类中的方法都是静态的,并且满足覆盖条件,但是仍然不会发生覆盖,因为静态方法是在编译的时候把静态方法和类的引用类型进行匹 配。
覆盖和重载的不同:
• 方法覆盖要求参数列表必须一致,而方法重载要求参数列表必须不一致。
• 方法覆盖要求返回类型必须一致,方法重载对此没有要求。
• 方法覆盖只能用于子类覆盖父类的方法,方法重载用于同一个类中的所有方法(包括从父类中继承而来的方法)。
• 方法覆盖对方法的访问权限和抛出的异常有特殊的要求,而方法重载在这方面没有任何限制。
• 父类的一个方法只能被子类覆盖一次,而一个方法可以在所有的类中可以被重载多次。
class FarClass{
private $myPv;
protected $myVar;
public function myFun(){
...
}
....
}
class SonClass extends FarClass{
private $myPv;
protected $myVar;
public function myFun(){
...
}
...
}
从上面的例子中可以看出, 类的私有变量不会产生覆盖。原因在于private成员无法继承下来。对于protected/pulic变量,则会产生覆盖问题。如果要在子类中访问父类变量,在java中可能使用super关键字,即:
class SonClass{
private $myPv;
protected $myVar;
public function myFun{}
public fuction myFun2{
//下面这名将会抛出一个异常,不能访问父类私有变量
//var0 = super.myPv;
var1 = super.myVar;
var2 = super.myFun();
}
...
}
PHP中
PHP的parent关键词只能用于调用父类的可继承方法(protected/public)及可继承静态变量,而对于非静态成员,则会抛出异 常:Fatal error: Access to undeclared static property。如:
class Far{
protected $myVar;
private function Fun1(){}
function Fun2(){}
}
class Son extends Far{
protected $myVar;
funciton Fun2(){
//下面句将会抛出异常,不能访问父类私有方法。
parent :: Fun1();
//下面会报错,Fatal error: Access to undeclared static property:
$var = parent :: $myVar;
//这一句正常运行,无论方法是否为静态方法。
parent :: Fun2();
}
}
如果要访问父类被覆盖的变量,通过上面的例子可以看出是不行的,那么,唯一的办法是在父类中增加一个方法,通过调用父类方法,从而取得被覆盖的父类变量。
3、JAVA中类的多态。
多态性的一般定义为:同一个操作作用于不同的类的实例,将产生不同的执行结果。在Java中,父类的变量可以引用父类的实例,也可以引用子类的实例。
我们来看例子:
class Father{
public void func1(){
func2();
}
public void func2(){
System.out.println("AAA");
}
}
class Child extends Father{
public void func1(int i){
System.out.println("BBB");
}
public void func2(){
System.out.println("CCC");
}
}
public class PolymorphismTest {
public static void main(String[]args) {
Father child = new Child(); //定义一个父类类型的引用指向一个子类的对象
child.func1();//输出CCC
child.func1(20)//没法通过编译
}
}
上面的程序是个多态的例子。子类Child继承了父类Father,并重载了父类的func1()方法,重写了父类的func2()方法。重载后的func1(int i)和func1()不再是同一个方法,由于父类中没有func1(int i),那么,父类类型的引用child就不能调用func1(int i)方法。而子类重写了func2()方法,那么父类类型的引用child在调用该方法时将会调用子类中重写的func2()。 所以对于多态,可以总结它为:
一、使用父类类型的引用指向子类的对象;
二、该引用只能调用父类中定义的方法和变量;(父类类型的引用可以调用父类中定义的所有属性和方法,而对于子类中定义而父类中没有的方法,它是无可奈何的, 同时,父类中的一个方法只有在在父类中定义而在子类中没有重写的情况下,才可以被父类类型的引用调用;)
三、如果子类中重写了父类中的一个方法,那么在调用这个方法的时候,将会调用子类中的这个方法;
四、它成立的条件为:要有继承、要有重写、父类变量引用子类对象。
从上面的例子可以看出,多态的一个好处是:当子类比较多时,也不需要定义多个变量,可以只定义一个父类类型的变量来引用不同子类的实例。请再看下面的一个例子:
public class Demo {
public static void main(String[] args){
// 借助多态,主人可以给很多动物喂食
Master ma = new Master();
ma.feed(new Animal(), new Food());
ma.feed(new Cat(), new Fish());
ma.feed(new Dog(), new Bone());
}
}
// Animal类及其子类
class Animal{
public void eat(Food f){
System.out.println("我是一个小动物,正在吃" + f.getFood());
}
}
class Cat extends Animal{
public void eat(Food f){
System.out.println("我是一只小猫咪,正在吃" + f.getFood());
}
}
class Dog extends Animal{
public void eat(Food f){
System.out.println("我是一只狗狗,正在吃" + f.getFood());
}
}
// Food及其子类
class Food{
public String getFood(){
return "事物";
}
}
class Fish extends Food{
public String getFood(){
return "鱼";
}
}
class Bone extends Food{
public String getFood(){
return "骨头";
}
}
// Master类
class Master{
public void feed(Animal an, Food f){
an.eat(f);
}
}
运行结果:
我是一个小动物,正在吃事物
我是一只小猫咪,正在吃鱼
我是一只狗狗,正在吃骨头
所以我们可以增加很多的子类,调用相同的方法通过传递不同的参数就可以引用不同子类的实例。
PHP通过设计模式的工厂模式可以实现多态应用,下面我引用PHP的工厂模式来说明
例子:
interface people {
function jiehun();
}
class man implements people{
function jiehun() {
echo '送玫瑰,送戒指!<br>';
}
}
class women implements people {
function jiehun() {
echo '穿婚纱!<br>';
}
}
class SimpleFactoty {
static function
createMan($operate) {
if($operate=='man'){
return new man();
} else if($operate=='woman')
return new women();
}
}
$man =SimpleFactoty::createMan('man');
$man->jiehun();//送玫瑰,送戒指!<br>
$man =SimpleFactoty::createMan('women');
$man->jiehun();//穿婚纱!
4,JAVA内部类实例化及分类
在Java 中,允许在一个类(或方法、语句块)的内部定义另一个类,称为内部类(InnerClass),有时也称为嵌套类(Nested Class)。
必须先有外部类的对象才能生成内部类的对象,因为内部类需要访问外部类中的成员变量,成员变量必须实例化才有意义,外部类要想访问内部类的成员变量和方法,则需要通过内部类的对象来获取。例:
public class Outer {
private int size;
public class Inner {
private int counter = 10;
public void doStuff() {
size++;
}
}
public static void main(String args[]) {
Outer outer= new Outer();
Inner inner= outer.new Inner();
inner.doStuff();
System.out.println(outer.size);
System.out.println(inner.counter);
// 编译错误,外部类不能访问内部类的变量
System.out.println(counter);
}
}
内部类可以是静态(static)的,可以使用 public、protected 和 private 访问控制符,而外部类只能使用public,或者默认。
1,成员式内部类
在外部类内部直接定义(不在方法内部或代码块内部)的类就是成员式内部类,它可以直接使用外部类的所有变量和方法,即使是private 的。外部类要想访问内部类的成员变量和方法,则需要通过内部类的对象来获取。
public class Outer{
private int size;
public class Inner {
public void dostuff() {
size++;
}
}
public void testTheInner() {
Inner in= new Inner();
in.dostuff();
}
}
成员式内部类如同外部类的一个普通成员。
成员式内部类可以使用各种修饰符,包括 public、protected、private、static、final 和abstract,也可以不写。
若有 static 修饰符,就为类级,否则为对象级。类级可以通过外部类直接访问,对象级需要先生成外部的对象后才能访问。
非静态内部类中不能声明任何static 成员。
2,局部内部类
局部内部类(Local class)是定义在代码块中的类。它们只在定义它们的代码块中是可见的。
局部类有几个重要特性:
仅在定义了它们的代码块中是可见的;
可以使用定义它们的代码块中的任何局部 final 变量;
局部类不可以是static 的,里边也不能定义 static 成员;
局部类不可以用public、private、protected 修饰,只能使用缺省的;
局部类可以是abstract 的。
例如:
public class Outer {
public static final int TOTAL_NUMBER = 5;
public int id = 123;
public void func() {
final int age = 15;
String str= "http://www.weixueyuan.net";
classInner {
public void innerTest() {
System.out.println(TOTAL_NUMBER);
System.out.println(id);
// System.out.println(str);不合法,只能访问本地方法的final变量
System.out.println(age);
}
}
new Inner().innerTest();
}
public static void main(String[] args) {
Outer outer= new Outer();
outer.func();
}
}