目录
方法调用时的参数传递原则
如果形式参数的数据类型是基本数据类型(数值型(byte、short、int、long、float、double)、字符型char、布尔型boolean),则实际参数向形式参数传递的值是副本;如果形式参数的数据类型是引用数据类型(数组array、类class、接口interface),则实际参数向形式参数传递的是引用值或地址值或对象的名字而不是对象本身。
即java中只有传值,不传对象。传值意味着当参数被传递给一个方法或者函数时,方法或者函数接收到的是原始值的副本。因此如果方法或者函数修改了参数,受影响的知识副本,原始值保值不变。
当传递的是对象的引用时,如果在方法中修改被引用对象的内容,这个改变会影响到原来的对象,对象的内容也就变了。而传递的如果是原始类型则不会有影响。
其实很像C++的引用传递和值传递
引用赋值并修改
接下来这个例子主要就是想要说明引用赋值并修改,实则是地址赋值,两个对象指向同一块地址。
package henu;
public class Test1 {
public static void main(String []args){
Flower flower1=new Flower("osmanthus","golden");
System.out.println("This is "+flower1.getName()+" in "+flower1.getColor());
Flower flower2=flower1;
flower2.setColor("silver");
System.out.println("This is "+flower1.getName()+" in "+flower1.getColor());
}
}
class Flower{
private String color;
private String name;
Flower(String name,String color){
this.color=color;
this.name=name;
}
void setColor(String color){
this.color=color;
}
String getColor(){
return this.color;
}
String getName(){
return this.name;
}
void setName(String name){
this.name=name;
}
}
这里值得一提的是一个java文件(就是一个后缀为java的文本)只能有一个pulic class,这里的Flower类就不能加public了!
String类
String 类是不可改变的,所以你一旦创建了 String 对象,那它的值就无法改变了。 如果需要对字符串做很多修改,那么应该选择使用 StringBuffer & StringBuilder 类。
每次对String的操作都会生成一个新的String对象。
StringBuffer 和 StringBuilder 类的对象能够被多次的修改,并且不产生新的未使用对象。
我们知道输出格式化数字可以使用 printf() 和 format() 方法。String 类使用静态方法 format() 返回一个 String 对象而不是 PrintStream 对象。
String 类的静态方法 format() 能用来创建可复用的格式化字符串,而不仅仅是用于一次打印输出。如下所示:
```java
System.out.printf("The value of the float variable is " +
"%f, while the value of the integer " +
"variable is %d, and the string " +
"is %s", floatVar, intVar, stringVar);
```java
String fs;
fs = String.format("The value of the float variable is " +
"%f, while the value of the integer " +
"variable is %d, and the string " +
"is %s", floatVar, intVar, stringVar);
System.out.println(fs);
String类的其他一些常用的方法有length、equals、replace、substring、toString等详细大家可以参考Java API文档
成员变量和局部变量初值、作用域
成员变量有默认初值、而局部变量则没有默认初值
类型 | 初始值 |
---|---|
boolean | false |
char | ‘\u0000’(空格)或’\0’ |
byte,short,int,long | 0 |
float | +0.0f |
double | +0.0 |
对象 | null |
成员变量的作用域为这个类,或在类体中的各个方法种都有效,而局部变量属于特定的方法,作用域为单个方法体
public class Exam_1{
int x;//成员变量,在方法种被同名局部变量x隐藏
public void exam(){
int y;//局部变量
System.out.println("x="+x);
//System.out.println("y="+y); //报错
}
public static void main(String []args){
…………
}
}
构造函数
- 成员方法通过对象或类调用或直接调用;构造方法通过new运算符调用
- 构造方法不能有返回值,因为它隐含的返回一个对象
- 在构造函数内可以调用构造函数,其他函数不能调用构造函数
this引用
this引用表示当前对象本身,确切地说,this代表了当前对象的一个引用。对象的引用可以理解为对象的另一个名字,通过引用可以顺利地访问对象
this引用的3中用法
-
在实例方法中(注意不是static方法中)中作为本类当前对象将其引用作为自变量传递给其他方法。
如-
en=new Event(this); 在实例方法中将当前对象引用作为自变量传递给其他方法
-
services.add(this); 将当前对象添加到等待服务队列中
-
return this;返回当前对象
-
System.out.println(this);输出当前对象,其自动调用超类或Object的toString()方法。
父类Object的toString方法:getClass().getName() + “@” +
Integer.toHexString(hashCode()); 打印的是“类名@hascode
-
-
this.成员变量、this.成员方法([参数列表])
这点大家想必都十分熟悉,不过多赘述 -
调用本类重载的构造方法
public Flower(String color){
this.color=color;
}
public Flower(String color,String name){
this(color); //注意这里this()应该为第一句,因为此前不存在任何对象。
//这个地方我也有点似懂非懂。
this.name=name;
}
static成员
Java类中的成员包括实例成员和类成员两种。类成员或静态成员用static修饰,而实例成员则不用static修饰。
某些成员希望独立于每个对象;或者说让每个对象都能使用;甚至没有创建对象时也能访问静态属性和静态方法。有时,人们不想父类的成员被子类覆盖,而只想让其被子类的成员隐藏。类变量类似于全局变量,类方法类似于全局方法
class Student{
public long idNum;
public String name;
private static long nextId=0; //注意这里类变量的使用
Student(){
idNum=nextId++;
}
public static void main(String []args){
System.out.println(new Student().idNum);
System.out.println(new Student().idNum);
}
}
类变量和实例变量:
类的所有实例共享类变量,每一个实例改变了类变量,都会永久的改变类变量,影响到其他的对象。
实例成员变量属于对象,必须通过对象引用;类成员变量属于类,一般通过类引用,用对象引用不规范,而且容易产生误解。
注:
类引用就是类名.类成员对象
实例引用就是实例对象.实例成员变量
类方法中不能使用this和super,因为其中没有可以操作的特定对象;不能创建内部类的实例;不能引用实例变量;不能直接调用实例方法,必须创建对象,用对象引用或调用.
就是说类方法只能直接访问类成员,如果要访问实例成员,需生成对象,用对象调用(就是在静态方法里new一个对象出来);实例成员方法即可访问类成员,也可以访问实例成员。
类的继承
子类自动拥有父类除构造方法与私有成员以外的全部成员,包括成员变量和成员方法。
Java类只有单重继承,没有多重继承。如果一个子类具有一个直接父类,这种继承关系称为单重继承。有多个直接父类则称为多重继承。Java类只要单重继承,接口有多重继承。
子类继承父类的非私有成员变量,包括实例成员变量和类成员变量
子类继承父类除构造方法以外的成员方法,包括实例成员方法和类成员方法
子类不能继承父类中用final修饰的方法(这点会在后面讲final时候详述)
子类不能继承父类中被隐藏的成员变量或被覆盖的成员方法
特征
- 子类可以重定义父类成员,包括隐藏父类的成员变量、静态方法和覆盖父类的实例方法。
- 子类中可以访问父类中非私用的被隐藏和被覆盖的成员方法。(通过super.成员方法)
- 在子类的构造函数中,可通过super()方法将父类的变量初始化。
- 当父类为抽象类时,子类可覆盖父类中的抽象方法,并在子类中实现该方法。
Object类
Java中的类都是Object的子类
public class People{ }等价于public class People extends Object{ }
Java中的每个类都从Object类继承了成员方法,比较两个对象的方法equals()、转换为字符串的toString()方法。(Object的toString方法上面又说到的)。
上转型
OK,重点来了。学过C++的一定都知道上转型!
先看一个简单的demo
public class Demo {
public static void main(String[] args) {
Patterning patterning1=new Circle(2);
getInfo(patterning1);
Patterning patterning2=new Square(1,2);
System.out.println(patterning2.getName()+"'area is "+patterning2.getArea()+" and the perimeter is "+patterning2.getPerimeter());
((Square) patterning2).setLen(2,3);//这里因为父类中并没有setLen函数,所以下转型到Square。
//上转型对象调用不了子类新增加的成员或方法,这点很重呀!!
getInfo(patterning2);
}
static void getInfo(Patterning p){ //一般上转型的代码,都会有这样的函数,把父类作为参数传进去
System.out.println(p.getName()+"'area is "+p.getArea()+" and the perimeter is "+p.getPerimeter());
}
}
class Patterning{
private String name="Patterning";
public String getName(){
return this.name;
}
public double getArea(){
return 0
}
public double getPerimeter(){
return 0;
}
}
class Circle extends Patterning{
private double r;
private String name="Circle";
Circle(double r){
this.r=r;
}
public double getArea() {
return Math.PI*r*r;
}
public double getPerimeter() {
return 2*Math.PI*r;
}
public String getName(){
return this.name;
}
}
class Square extends Patterning{
private String name="Square";
private double length;
private double width;
Square(double length,double width){
setLen(length,width);
}
public public void setLen(double length,double width){
this.length=length;
this.width=width;
}
public double getArea() {
return length*width;
}
public double getPerimeter() {
return 2*(length+width);
}
public String getName(){
return this.name;
}
}
对象的上转型即声明一个父类类型的变量,但将子类的实例赋给它。或叫父类变量指向子类对象。
对于成员,如果父类有,子类没有则子类继承;如果父类没有,子类有,则属于子类增加的;如果父类子类都有,便属于隐藏或覆盖。其中对于成员变量或静态方法,属于隐藏;对于实例方法,属于覆盖。一般的,子类对象调用子类的成员,父类对象调用父类的成员。
但对于上转型的对象,则调用成员变量和静态方法时调用的是父类的,因此叫做隐藏成员变量和静态方法;调用实例方法时调用的是子类的,因此叫覆盖实例方法。
上转型对象调用不了子类新增加的成员或方法!!!
public class Test1{
public static void main(String []args){
A a=new A();
B b=new B();
A c=new B();
System.out.println("a.i="+a.i+","+"a.j="+a.j+",a.plus()="+a.plus());
System.out.println("b.i="+b.i+","+"b.j="+b.j+",b.plus()="+b.plus());
System.out.println("c.i="+c.i+","+"c.j="+c.j+",c.plus()="+c.plus());
}
}
class A{
int i=1,j=2;
int plus(){
return i+j;
}
}
class B extends A{
int i=10,k=20; //i与父类同名,隐藏了父类的i
int plus(){ //覆盖了父类的同名方法
return i+j+k;
}
}
a.i=1,a.j=2,a.plus()=3
b.i=10,b.j=2,b.plus()=32
c.i=1,c.j=2,.plus()=32 //注意c.plus()的j是B的j!!!
子类继承父类时的变量类型
- 子类继承父类方法时,子类对象调用父类方法,父类方法中引用的是父类的成员变量,甚至是私有变量。(意思是说子类没有重写父类的方法,继承了父类的方法,则当子类对象调用该方法时,方法中引用的变量都是父类的变量)
- 子类实例方法覆盖父类实例方法时,子类对象调用的是子类方法,方法中的成员变量是子类的。(子类中重写了父类的方法,当子类对象调用该方法时,方法中引用的变量是子类的。)
- 多态时,即父类引用指向子类对象时,对象调用同名方法,调用的是子类的方法。(上转型时)
这就是上篇末尾疯狂打感叹号的地方
super引用
super在程序中表示当前对象的父类,是对当前对象的父类对象的引用。在Java中,当子类的变量或方法隐藏了父类中的变量或静态方法,而在子类中又要引用父类的成员时,就要使用super。
super的用法
- 调用父类的构造方法。子类不能继承父类的构造方法,在子类的构造方法体中,可以使用super调用父类的构造方法
super([参数列表]); - 引用父类同名的成员。当子类重定义了父类成员时,则存在同名成员问题。此时在子类方法体中,成员变量和成员方法均默认为子类的成员变量或成员方法。如果需要引用父类的同名成员,则需要使用super引用。(当子类覆盖父类实例成员方法或子类隐藏父类成员变量和静态方法,可以使用super. 进行调用)
不知道你是否有些许问号,就是覆盖和隐藏到底有什么区别呢?覆盖和隐藏都是子类重写了父类的某些东西,覆盖是子类重写了父类的实例成员方法,隐藏是子类重写了父类的成员变量和静态方法。
我是这样理解的,在上转型中,子类调用实例成员方法时,调用的是子类的实例方法,因为父类的成员方法被覆盖了。子类调用成员变量或静态方法时,调用的是父类的,因为子类的成员变量和静态方法被隐藏了。(可能不太准确啊,如果有错误,请一定指正!!)
几点需要注意的地方
- super不能像this一样单独使用,如return this; this=a;
- super只能在子类使用(这一点似乎显而易见)
- super不能引用父类的私用成员(这点需要注意!!)
- super不能用在静态方法中,与this一样。因为没有所指的对象。
abstract修饰的类
很像C++的virtual,相信你理解起来一定非常轻松
使用关键字abstract声明的类称为抽象类
Public abstract class Graphics{
//图形类,抽象类
public abstract double area();
//计算面积,抽象方法,只写定义不写实现!分号必不可少!
}
为什么要有抽象类呢?
想想在上一篇,上转型部分。那个demo中的父类Patterning中的getArea和getPerimeter方法的实现,直接return 0;所以这个方法体的实现并没有什么意义,在抽象类中,就无需写方法体的实现了。
抽象类的性质
1.抽象类不能够被实例化。
2.抽象类中可以不包含抽象方法,但包含抽象方法的类必须声明未抽象类。(不会像接口一样默认变成抽象方法,如果不是抽象方法,子类继承可以不用重写)
抽象方法的性质
1.抽象方法必须被子类覆盖
2.不能将构造方法、类成员方法声明为抽象方法
3.abstract抽象方法不能和static同用
4.父类的非抽象方法可以被子类的抽象方法覆盖
这里是一个抽象类去继承一个非抽象类,重写父类的方法,不常用
可以自己试着去把上篇上转型部分的demo用抽象类修改一下。Java对象的还有最后一更,会过几天吧。这几天要研究一下Android开发和CTF招新赛了!>_>,加油!!
final修饰
final修饰变量,对基本数据类型来说其值不可变,而对于对象数据类型来说其引用不可以变
final修饰类,使用关键字final声明的类为最终类,最终类不能被继承,即不能声明最终类的子类。最终类中的成员变量可以使final的,也可以不是final的。但最终类的方法自动变成final的
final修饰对象。一个对象是常量不代表不可以改变对象的成员,仍可以对其成员进行操作
final Object obj=new Object();
obj=new Object(); //这就会报错
final修饰方法。使用final声明的成员方法称为最终方法。最终方法不能被子类覆盖
final修饰成员变量时,该变量为常量,必须赋以初值(可在声明的时候赋值或在类的构造方法中赋值);当final修饰局部变量时,该局部变量只能被赋一次值
//这里就直接拿了别人的代码
package com.lic.array;
public class FinalInstanceVaribaleTest {
public static void main(String[] args) {
FinalInstanceVaribaleTest fiv = new FinalInstanceVaribaleTest();
System.out.println(fiv.var1);
System.out.println(fiv.var2);
System.out.println(fiv.var3);
}
// 定义final实例变量时赋初始值
final int var1 = "疯狂Java讲义".length();
final int var2;
final int var3;
// 在初始化块中为var2赋初始值
{
var2 = "轻量级Java EE企业应用实战".length();
}
// 在构造器中为var3赋初始值
public FinalInstanceVaribaleTest(){
this.var3 = "疯狂XML讲义".length();
}
}
接口
接口相当于一种特殊的抽象类,一个类可以实现多个接口,以此实现C++中多继承的功能。
声明接口
[public] interface接口 [extends 父接口]{
[public] [static] [final] 数据类型 成员变量=常量值;
[public] [abstract] 返回值类型 成员方法[(参数列表)];
}
声明实现接口的类
[修饰符] class类<泛型> [extends 父类] [implements 接口列表]
接口的特点
- 接口中的方法默认是public和abstract的(可省略)。但类在实现接口方法时一定用public修饰。接口中的成员方法都是抽象的实例成员方法。不能用protected、private、final、static修饰它们
- 接口中的所有抽象方法必须全部被实现接口的类或其子类覆盖
- 接口中的成员变量都是常量,接口中成员变量的默认修饰符为public static final
- 接口不能被实例化
- 接口是引用数据类型
我觉着最主要的几个点就是这些,当然还有其他的一些特点
那么问题来了,要选择用抽象类还是要用接口呢?
- 如果不需要定义成员变量和方法,则优先使用接口而不是抽象类。只有在不得不得具体定义成员变量和方法的情况下才考虑用抽象类或实体类。
- 变更已经存在的功能的时候用继承,增加新功能的时候用接口
- 当几个人写一个模块的时候,接口有利于并行开发,另外接口有利于单元测试
- 对于任意两个类,不管它们是否属于同一个父类,只要它们存在相同的功能,就能从中抽象出一个接口类型。对于已经存在的技能树,可以方便的从类中抽象出新的接口,但从类中抽象出一个新的抽象类却不那么容易,因此接口更有利于软件的维护与重构
多态
其实呢,不知不觉中,常常为人称道的多态你已经学完了!是不是很神奇!
多态的具体表现:
- 方法的重载
- 实例成员变量和静态成员变量的隐藏
- 实例方法的覆盖
- 动态绑定(上转型对象调用父类的变量和静态方法,而调用子类的实例方法)
JAVA对象到这里就结束了,如果有问题,还望指正!感谢观看!