======================================= 华丽的分割线 ====================================2013-12-28 日 更新
我之前有看过孙鑫的JAVA视频,虽然只看了前三集,但是孙鑫老师的视频是压缩讲的,一个视频两个半钟,设计到的知识点很多,
前三个视频已经包含了《Java4Android》这套视频前二十三集的内容,而且讲的都是原理性的知识。只是孙鑫老师讲的比较死板,
举的例子不够生动。而 《Java4Android》mars老师讲的很生动,可惜都是表面性的东西,例如他的第十七集,对于静态变量、
静态函数的为什么不创建对象就能调用的解释。而孙鑫老师在第二集中讲的很明确。关于第十七集的心得就是看了孙鑫老师才知道的。
虽然说Java不是我日后的方向,但是既然日后会用上,现在又恰好在学,即使不精通JAVA也要达到熟练的程度吧?不然日后还怎么学
安卓编程。 所以我决定放弃《Java4Android》重学孙鑫老师的JAVA视频。日后发布的也发布的也将是学孙鑫老师JAVA视频的学习笔记。
唉,悲剧,这两套视频的教学思路不同,知识点的解析而理解也不同,只能重新学孙鑫老师的了。
翻到下面就能看到第21-22集的笔记。
======================================= 华丽的分割线 ====================================
这学期工作太忙,学单片机的时候总是被中断,让我很不爽。后来越来越忙就没有再学了,这段时间还算缓下来了,不过我不想再在我非常认真学单片机的过程中被频繁中断,想着日后会用到安卓手机控制单片机,由于学安卓编程需要有JAVA基础,就先接触一下JAVA基础知识,就当充实充实自己的业余生活。
本文记载一些心得中的重点部分,仅用于增强记忆和日后复习使用。 这视频不错,讲的很生动。搞的我看的就像是看电影一样总想一直看下去,缺点就算说的太详细了。。。 第1集至第3集 讲的是Java的一些历史和一些基本的概念,如JDK、JRE、JVM这些。 这里有必要说一下,Java之所以能实现跨平台是因为JVM虚拟机的存在。我们写的源代码(.java)不需要关心它会在什么平台下执行,它会被编译成中间文件(.calss),而这个中间文件是和平台无关的,主要是给JVM解释执行的。只要该平台支持JVM,那么也就支持Java了,每一个平台下的JVM都不一样,但是它们都可以解释并执行中间文件。Windows平台有Windows平台的JVM,Linux有Liunx下的JVM,说白了,只要能在某一平台下设计出支持该平台的JVM,那么该平台就可以全面支持Java了,而JVM的设计是由Java公司自己完成,我们不需要关心。这就是Java跨平台的秘密。 第4集: 有意思的变量解释:(之所以提出来是因为该视频讲的很有意思) 1、计算机是一种极度精确的机器,容不得半点马虎。 2、它很笨,要计算数据时,首先要指定数据存储在内存的位置以及空间大小。 基于上述两点,将一些常用的数据类型都固定大小,如整型(int)占用4个字节,字符型(int)占用2个字节,byte型占用1个字节等等,之所以整出这些基本数据类型都是为了方便写程序,例如你要存放一个 1025 数值或者你要存放一个字符“梦”,就不需要自己指定要申请多大的内存空间了。 变量的声明含义: int age; int:变量类型,也就是指明空间大小 age:变量名,存放在内存中的位置标识,这里具体放在哪个位置由系统负责,系统分配好空间后会将该位置与我们的变量关联起来,相当于给这个位置起一个名字,我们要把数据存放到age,就表明把这个数据放到age所在的内存空间位置. 后面的分号是标识这条语句已经结束。 第5集到第11集 主要是一些基本的程序流向控制 如判断语句 分支语句 循环语句等。 第12集至第15集 主要面向对象的一些概念: 这集主要解释什么是面向对象。 这集给我的冲击很大,以前我是用C语言写代码,使用的面向过程的思维去设计程序。面向对象对于我来说是一个全新编程思维。 刚接触这种思维,可能是因为以前习惯性用面向过程的思维去设计程序,现在有些转不过弯来。 1、首先确定谁来做,其次确定怎么去做。 2、首先考虑全局,再考虑局部。 3、首先考虑抽象,再考虑具体。 如: 我们用面向对象的思维去给一栋建筑设计一座电梯。首先我们先抽象有这个电梯,然后这个电梯有高度、宽度、有门、有按键的属性,它还会有开门、关门、上升、下降等行为。确定好这些后,那么再考虑开门的方法(行为),这门要在什么时候开,怎么开才安全,门在什么时候后关,怎么关才安全,电梯什么时候上升、什么时候下降,怎么上升、怎么下降,要上升或下降到第几层等这些行为(这时候相当于定义好这个类)。定义好这些后,再根据这栋建筑去具体化这个电梯,例如这建筑有几层楼,每层楼高度是多少等,这关乎电梯的上升、下降的高度等。(这个时候相当于引用电梯这个类创建出这个对象,并初始化这个对象的一些参数)。不同的建筑需要具体化合适这栋建筑的电梯。 第13集 类和对象 类是抽象的,对象是具体的。 类里面有状态、行为。 类里面的变量就是记录状态,而里面的函数则是行为(方法)。 举个例子: 类相当于是指明狗这种种类的一系列特征:身高、体重、毛色等一些属性,还有怎么吠叫、怎么吃饭等一些行为。它是一个抽象的东西,就好像我们说到狗,脑子里面就会想起狗的这些特征,满足这些特征才会被我们认为是狗,但并不具体到是哪一种狗,哈巴狗?藏獒?还是流浪狗?它们都是狗。 而对象则是具体到哪一种狗,藏獒和哈巴狗叫的声音不太一样,跑的也不一样,喜欢吃的食物也不同。 类和对象就好像我们需要一条狗(狗这种种类),这就选择了狗这个类。至于是什么狗,要看自己的需求,假设我们要一条藏獒,那么它应该是很彪悍的,发出的叫声应该是让人很害怕的。那么这时候我们创建这个类的对象,设置这只狗的身高、体重、毛的颜色等一些属性,然后我们要这只狗吠叫,就使用里面吠叫的方法。 定义类的方法: Class 类名 { 属性;—> 属性也叫成员变量,用来描述类的状态,如上面狗的身高、体重、毛色等。 方法;—> 方法也叫成员方法,用来描述类的行为,如上面狗的吠叫,吃饭等。。。 } 生成对象的方法和流程。 例:Dog dog = new Dog(); Dog dog:系统会在栈内存空间创建Dog的一个引用名,为dog。 new Dog():系统会在堆内存空间创建Dog的一个对象。 =:赋值号就是把在堆内存空间创建的Dog对象关联到dog 这个引用名。 当我们使用的时候就是需要用引用名去操作这个对象。
第14集:
下面这个例子很形象的表现出类与对象的关系。
class Dog // 定义狗这一类 { String Zhonglei; // 狗的品种 String name; // 狗的名字 int age; // 狗的年龄 String color; // 狗的毛色 String Xingbie; // 性别 void jump() { System.out.Println(“跳啊跳啊。。。我拼命跳。。。”); } } class Test { Public static void main(String args[]) { Dog Mydog = new Dog(); // 创建一只狗 Mydog.Zhonglei = “牧羊犬”; // 我喜欢这品种 Mydog.name = “小黑”; // 这狗就叫小黑吧。 Mydog.colcr = “黑色”; // 它的毛色是黑色的 Mydog.age = 1; // 它1岁了
Mydog.Xingbie = “雄性”; // 公的 Mydog.jump(); // 这只1岁的黑色小黑跳起来了。 Dog Mydog1= new Dog(); // 小黑太孤单了,给它创建个伴侣吧。 Mydog.Zhonglei = “牧羊犬”; Mydog.name = “小白”; // 这狗就叫小白吧。 Mydog.colcr = “白色”; // 它的毛色是白色的 Mydog.age = 1; // 它也是1岁。
Mydog.Xingbie = “雌性”; // 母的 Mydog.jump(); // 这只1岁的白色小狗也开心跳起来了。 } } 用着这种设计思想,感觉自己就像是上帝,哈哈。 Mydog 和 Mydog1 都是狗,但它们不是同一只狗,而是两只不太一样的狗。像上面的例子,还可以再创建一只藏獒(创建对象),藏獒和牧羊犬都是同一个物种,但却不是同一个品种。很灵活。 Dog就是抽象的,而创建的对象则是具体的。就上面的例子来说,栈空间内会存放两个对象名,堆空间内存放着两个对象。 匿名对象: 如:new Dog().jump(); 因为没有给引用名,所以它是一次性的,不知道匿名对象算不算是创建了一只狗又扔了它。。。真狠心。。。。。
第15集:
函数重载:一个类中的有多个函数名一样,但参数数量、类型不同的函数。系统会根据我们所传递的参数去判断我们要调用的是哪一个函数。 构造函数:必须和类名相同,利用函数重载,可以有多个构造函数。构造函数会在new创建一个对象时自动被调用,调用哪个构造函数取决自己new 类名(参数)所传递的参数,一般用于给成员变量赋初始值。如果没有写构造函数,编译器会自动添加一个无参数、无函数体的空构造函数。构造函数是木有返回值滴。 第16集 (this的作用和使用方法)
this(参数):调用本类中构造函数,只能写在函数中的第一句。
this.变量:在函数中访问本类中的变量,一般区分函数中有和类中同名的变量。
this.函数:调用本类中的函数。
第17集(关于静态 static)
静态变量和静态函数可以在不new(创建)对象的情况下直接调用。 例: class Test { static int i ; static void fun() { System.out.println(“static fun Run....”); } } class main { public static void main(String args[]) { Test.i = 10; Test.fun(); } } 被声明为static的变量或函数都会在该类被装载时会被创建在堆栈里。也就是说不需要我们new,内存中已经有它们的存在。而且它们都是被所有这类的对象所共享。 静态变量或静态函数,之所以可以在未用new在堆空间里创建新的对象时,却仍可以通过类名使用,是因为它在程序运行时已经在堆内存分配好空间,也就是说无论有没有new,那空间都已经分配好并且不会变动。 如视频中的例子:在Person类里面的static int i; 在类第一次被装载时 i 这个变量已经在堆空间分配好并且不会变动,所以当我们使用这个静态变量时不需要重新new对象,即使new了对象,也不会再为它在堆空间重新分配内存,无论我们new了多少个Person对象,它们都是共享同一个 i 空间。所以当改动 i 的值时,自然而然的看起来是像影响到其他对象里面 的 i 值。 而 int i;这种非静态变量则必须是在调用new时才会在堆空间分配好内存。而且每次new的空间位置都不一样,都是一份拷贝,每一份拷贝都关联不同的Person对象,这样就变成即使修改了第一个Person对象里的i,也不会影响第二个Person对象里面的i。
关于匿名静态函数:
匿名静态函数没有名字的函数。 static { System.out.println(“我是匿名静态函数”); } 匿名静态函数在使用类的时候会被调用,而构造函数则是在new时被调用。
1、Test.i = 10 或 Test.fun() 匿名静态函数会被调用
2、Test t = new Test() 匿名静态函数会被调用后,构造函数才会被调用。
第18集 继承
通过关键字 extends 来继承某一类,被继承的类为父类,继承的类为子类。 子类拥有父类所有的成员变量和成员函数,但不拥有构造函数。 子类可以有自己的成员变量和函数,感觉是在父类的基础上是增加状态和行为。是父类的一种扩展。 例子:
class myzl extends Gog // myzi 是子类的名字,Gog是父类,是已经存在的类 { } 第19集super关键字
由于子类是不能继承父类的构造函数,所以可以使用super这个关键字去调用父类的构造函数,当然这个super不但可以调用父类的构造函数还可以在父子类之间函数名、变量相同的情况下,在子类调用父类的函数或访问父类的变量。super和this差不多,都是用于区分同名函数或变量。 super() :调用父类的无参构造函数 super (参数):调用父类的有参构造函数 super和this的异同:(网上资料)
1)super(参数):调用基类中的某一个构造函数(应该为构造函数中的第一条语句)
2)this(参数): 调用本类中另一种形成的构造函数(应该为构造函数中的第一条语句)
3)super:它引用当前对象的直接父类中的成员(用来访问直接父类中被隐藏的父类中成员 数据或函数,基类与派生类中有相同成员定义时如:super.变量名,super.成员 函数据名(实参)
4)this:它代表当前对象名(在程序中易产生二义性之处,应使用this来指明当前对象;
如果函数的形参与类中的成员数据同名,这时需用this来指明成员变量名)
5)调用super()必须写在子类构造方法的第一行,否则编译不通过。每个子类构造方法
的第一条语句,都是隐含地调用super(),如果父类没有这种形式的构造函数,那么
在编译的时候就会报错。
6)super()和this()类似,区别是,super()从子类中调用父类的构造方法,this()在同一类内
调用其它方法。
7)super()和this()均需放在构造方法内第一行。
8)尽管可以用this调用一个构造器,但却不能调用两个。
9)this和super不能同时出现在一个构造函数里面,因为this必然会调用其它的构造函数,
其它的构造函数必然也会有super语句的存在,所以在同一个构造函数里面有相同
的语句,就失去了语句的意义,编译器也不会通过。
10)this()和super()都指的是对象,所以,均不可以在static环境中使用。包括:static变
量,static方法,static语句块。
11)从本质上讲,this是一个指向本对象的指针, 然而super是一个Java关键字。
第20集 函数的override(覆盖、重写) 需要满足两个条件: 1、在具有父子关系的两个类当中。 2、父类和子类都有一个同定义的函数(返回值类型、函数名和参数列表等完全相同) 当父类中的某A函数完全无法满足我们的要求时,我们可以在子类上重写父类的A函数,这个A函数会完全覆盖父类的A函数,当执行A函数时,父类的函数是不会被执行的。 当然,很少情况下会遇到父类A函数完全无法满足我的要求,若是遇到还不如重新写一个函数。当然,如果是修改以及完成的源码,因为后面的代码已经写好调用了这个A函数名,那么这时候我们可以重写A函数,而不必要修改后续的代码调用。 一般遇到的是父类的A函数能满足一些基本的要求,但还是缺少一些功能,这时候我们可以先通过super.A() 来直接调用父类的代码,然后下面再添加需要的一些功能,这样当子类的A函数被调用时,会执行super.A(),而super.A()会是调用父类的A函数,那么父类的A函数执行完后就会执行接下来我们写的代码了。 例: class Person { String name; int age; void OutMsg() { System.out.println("父类 OutMsg"); System.out.println("我的名字是" + name + "我的年龄是" + age); } } class Student extends Person { String address; void OutMsg() { // 若这里是和父类的代码一样则可以换成 super.OutMsg(); // 这样就可以直接调用父类的OutMsg函数,避免重复代码 System.out.println("子类 OutMsg"); System.out.println("名字是" + name + "年龄是" + age); // 这是子类新增加的代码 System.out.println("我的地址是" + address); } } class Test { public static void main(String[] args) { Student s = new Student(); s.name = "张三枫"; s.age = 20; s.address = "广东"; s.OutMsg(); } } 输出: D:\MyJava>javac *.java D:\MyJava>java Test 子类 OutMsg 名字是张三枫年龄是20 我的地址是广东
================================================ 华丽的分割线 ================================================
2013-12-28 日 更新
[color=#3306f9,strength=3)"]================================================ 华丽的分割线 ================================================[color=#3306f9,strength=3)"]
第21集 对象的转型
这一集特让我纠结!作者说的不是很详细,而且给出的结论也跟我自己做实验的不符。最最最重要的是,作者介绍了对象的转型却没有举个例子说明其有什么作用。让我感觉这玩意是不是没啥用的。。。
对象向上转型:将子类的对象赋值给父类的引用。
如:Student(子类)继承 Person(父类)
Student s = new Student(); // 创建一个子类对象 s指向这个对象
Person p = s; //通过子类的引用将Student对象赋值给父类的引用
此时s和p指向的是同一个对象。那么是否能把p当成s去使用呢?看下面的例子。
class Person
{
String name;
int age;
String yiyang;
// 父类有的
void Person_fun()
{
System.out.println("父类:Person_fun执行");
}
void OutMsg()
{
System.out.println("父类:OutMsgyiyang = "+yiyang);
System.out.println("父类:我的名字是" + name + "我的年龄是" + age);
}
}
class Studentextends Person
{
String address;
String yiyang; //和父类同名的
void Student_fun()
{
System.out.println("子类:Student_fun 执行super.yiyang = " + super.yiyang);
}
void OutMsg()
{
super.OutMsg(); // 调用父类的OutMsg函数
System.out.println("子类:我的地址是address=" +address + " yiyang=" + yiyang); //这是子类新增加的代码
}
}
class Test
{
publicstatic void main(String[] args)
{
System.out.println("---------------邪恶的分割线-------------");
System.out.println("以下是父类的对象操作。。。。");
System.out.println("------------------------------------------");
Person p1 = new Person();
p1.yiyang = "父类yiyang";
p1.name = " 父类";
p1.age = 21;
//p1.address = "广州"; // 由于Person类没有这个变量 当然编译报错
p1.OutMsg(); //直接调用Person类的函数OutMsg();
//p1.Student_fun(); // 由于Person类没有这个函数 当然编译报错
p1.Person_fun(); //直接调用Person类的函数OutMsg();
System.out.println("\n\n---------------邪恶的分割线-------------");
System.out.println("以下是子类的对象操作。。。。");
System.out.println("------------------------------------------\n");
Student s1 = new Student();
s1.yiyang = "子类yiyang"; // 与父类同名变量,赋值的是子类的变量
s1.name = " 子类"; // 子类没有。赋值的是父类的变量
s1.age = 22; //同上
s1.address = "深圳"; // 父类没有,赋值的是子类的变量
s1.OutMsg(); //该函数被重写(覆盖)所以调用的是子类的函数
s1.Student_fun(); //子类独有,调用的是子类的
s1.Person_fun(); // 子类没有,调用的是父类的
System.out.println("\n\n---------------邪恶的分割线-------------");
System.out.println("以下是对象向上转型操作。。。。");
System.out.println("------------------------------------------\n");
Student s = new Student();
Person p = s; // 对象的向上转型
p.yiyang = "上转—yiyang"; // 与子类同名,实际上赋值的是父类的成员变量
p.name = " 对象向上转型"; //只有父类有这个成员变量 赋值的是父类的成员变量
p.age = 23; //赋值的是父类的成员变量 赋值的是父类的成员变量
//p.address = "广东"; // 在父类没有这个成员变量,编译会报错这行
p.OutMsg(); // 被复写(覆盖)的函数,调用的是子类的OutMsg()
//p.Student_fun(); // 在父类没有这个成员函数,编译会报错这行
p.Person_fun(); //可以调用父类的成员函数
}
}
用java编译输出:
如果没有红色字体没有被注释则输出:
D:\MyJava>javacTest.java
Test.java:14: 错误: 找不到符号
p1.address = "广州"; // 由于Person类没有这个变量 当然编译报错
^
符号: 变量 address
位置: 类型为Person的变量 p1
Test.java:17: 错误: 找不到符号
p1.Student_fun(); // 由于Person类没有这个函数 当然编译报错
^
符号: 方法 Student_fun()
位置: 类型为Person的变量 p1
Test.java:49: 错误: 找不到符号
p.address = "广东"; // 在父类没有这个成员变量,编译会报错这
行
^
符号: 变量 address
位置: 类型为Person的变量 p
Test.java:52: 错误: 找不到符号
p.Student_fun(); // 在父类没有这个成员函数,编译会报错这
行
^
符号: 方法 Student_fun()
位置: 类型为Person的变量 p
4 个错误
如果注释了红色字体则成功被编译。输出的执行结果是:
D:\MyJava>java Test
--------------- 邪恶的分割线 -------------
以下是父类的对象操作。。。。
------------------------------------------
父类:OutMsgyiyang = 父类yiyang // 这个就不用解释了,非常普通的类操作
父类:我的名字是 父类我的年龄是21
父类:Person_fun 执行
--------------- 邪恶的分割线 -------------
以下是子类的对象操作。。。。
------------------------------------------
父类:OutMsgyiyang = null // 这里的yiyang是父类的,没给父类yiyang赋值自然是null
父类:我的名字是 子类我的年龄是22 //子类没有同名变量,所以是给父类赋值
子类:我的地址是address=深圳 yiyang=子类yiyang
子类:Student_fun执行super.yiyang = null //同名变量,赋值的是子类的,而不是父类的
父类:Person_fun 执行
--------------- 邪恶的分割线 -------------
以下是对象向上转型操作。。。。
------------------------------------------
父类:OutMsgyiyang = 上转—yiyang
父类:我的名字是 对象向上转型我的年龄是23
子类:我的地址是address=null yiyang=null //可以发现子类的变量并没有被赋值
父类:Person_fun 执行
我们可以总结一下,对象的向上转型,在访问变量和函数的时候,和直接操作父类对象相比,当父类对象调用不存在的函数和变量时,编译器直接报错。向上转型对象也是。唯一和直接操作父类对象不同的是,在调用被子类重写(覆盖)的函数时,向上转型对象操作的是子类的函数,而操作父类对象则是直接调用父类的函数。
下面是作者给出的结论:
一个引用能够调用哪些成员(变量和函数),取决于这个引用的类型
一个引用调用的是哪一个方法,取决于这个引用所指向的对象
(让我郁闷的是,函数和方法不是指一样东西么,很矛盾。而且第二个结论是有前提的,那就是那个方法(函数)要被子类重写过才成立.)
下面我们看看对象的向下转型,由于视频并没有做过多介绍,所以唯有自己做实验。
向下转型 – 将父类的对象赋值给子类的引用
例:Student s1 =new Student();
Personp = s1;
Students2 = (Student)p;
或:Person p =new Student();
Students2 = (Student)p;
在Test.java文件中加入以下代码:
class Test
{
publicstatic void main(String[] args)
{
//......
//这代码和上面的一样,就不写出了。
//......
System.out.println("\n\n---------------邪恶的分割线-------------");
System.out.println("以下是对象向下转型操作。。。。");
System.out.println("------------------------------------------\n");
Studentss = new Student();
Personpx = ss;
Studentsx = (Student)px;
sx.yiyang= "下转-yiyang";
sx.name= "对象向下转型";
sx.age= 25;
sx.address= "惠州";
sx.OutMsg();
sx.Student_fun();
sx.Person_fun();
}
}
看看输出的结果:

发现向下转型和直接使用子类没啥区别。。。擦......
|