第一讲 继承
(1) 概述:把多个类中的相同的属性和行为进行抽取,封装到一个类中, 然后再建立新类的时候,不需要从头做起,继承刚才定义的那个类即可。
个人理解:多个类中存在相同属性和行为时,将这些内容抽取到单独一个类中,那么多个类无需再定义这些属性和行为,只要继承那个类即可。多个类可以称为子类,单独这个类称为父类或者超类。子类可以直接访问父类中的非私有的属性和行为。
(2)好处:
A:提高代码的复用性。
B:让类与类之间产生了一个关系,是多态的前提。
(3)什么时候使用继承?
A:如果类之间存在着:is a 的关系就可以考虑使用继承。
B:不要为了继承部分功能,就去使用继承。
(4)继承的特点:
A:Java只支持单继承,不支持多继承。 理解为:即一个类只能有一个父类,不可以有多个父类。
为什么呢? 因为如果支持多继承,就会有调用不明确的问题。
class SubDemo extends Demo{}//ok
class SubDemo extends Demo1,Demo2...//error
B:Java支持多层(重)继承。
class A{}
class B extends A{}
class C extends B{}
(5)super关键字
super是一个关键字,代表父类的存储空间标识。(可以理解为父亲的引用)
super的用法和this相像,那么他们有什么区别呢?
从三个方面总结为:
a:成员变量 this.变量-------本类的 super.变量------父类的
b:构造方法 this(...)------本类的 super(...)------父类的
c:成员方法 this.方法名() ----本类的 super.方法名()-----父类的
下面是一个综合运用super的例子,有两个类:一个Father类,一个Father类的子类Son,通过这两个类完全演示了super的用法,一下是代码:
public class Father {
public String s="Father";
public String x="输出了Father类的public成员变量x!";
public Father()
{
System.out.println("Father 构造方法被调用!");
}
public Father(String s)
{
this.s="Father类的带参构造方法运行了";
}
public void outinfo(){
System.out.println("Father 的outinfo方法被调用");
}
}
class Son extends Father{
public String s="Son";
public Son(){
super(); //调用超类的构造方法,只能放到第一行。
System.out.println("Son无参构造方法被调用!");
//super(); //错误的, 必须放到构造方法体的最前面。
}
public Son(String str) {
super(str);
System.out.println("Son带参构造方法被调用!");
}
//覆盖了超类成员方法outinfo()
public void outinfo()
{
System.out.println("Son的outifo()方法被调用");
}
public void test(){
String s="Hello!"; //局部变量s覆盖了成员变量s和超类变量s
System.out.println("************");
System.out.println(s); //输出局部变量s
System.out.println(this.s);// 输出(子类)成员变量s
System.out.println(super.s); //输出超类成员变量s
System.out.println("************");
System.out.println(x);//输出超类成员变量x,子类继承而来
System.out.println(super.x);//输出超类成员变量x
System.out.println("******************");
outinfo();//调用子类的outinfo()方法
this.outinfo();//调用子类的outifo()方法
super.outinfo();//调用父类的outifo()方法
}
public static void main(String[] args) {
new Son().test();
}
}
子类Son运行结果:
说明:此例子仅仅为了说明super的用法,实际在设计类的时候一般都尽可能私有(private)化。
通过上面的例子,下面总结一下super的用法:
第一、在子类构造方法中要调用父类的构造方法,用“super(参数列表)”的方式调用,参数不是必须的。同时还要注意的一点是:“super(参数列表)”这条语句只能用在子类构造方法体中的第一行。
第二、当子类方法中的局部变量或者子类的成员变量与父类成员变量同名时,也就是子类局部变量覆盖父类成员变量时,用“super.成员变量名”来引用父类成员变量。当然,如果父类的成员变量没有被覆盖,也可以用“super.成员变量名”来引用父类成员变量,不过这是不必要的。
第三、当子类的成员方法覆盖了父类的成员方法时,也就是子类和父类有完全相同的方法定义(但方法体可以不同),此时,用“super.方法名(参数列表)”的方式访问父类的方法。
(6) 函数覆盖(Override)
概述:子类中出现与父类一模一样的方法时,会出现覆盖操作,也称为 重写或者复写。
在这儿先总结一下子父类中成员变量和成员方法在名称相同时的用法:
成员变量名称相同: 子类对象在使用的时候,先找子类局部范围,再找子类成员范围,最后找父类成员范围。
成员方法名称相同:子类对象在使用的时候,先找子类的,再找父类的 。另外子类中出现与父类的一模一样的方法时,会出现覆盖操作,也称为重写或者复写。
子父类方法重写的注意问题:
1、父类的私有方法不可以被重写。
2、子类方法访问权限一定要大于等于父类方法访问权限。
3、静态的方法只能被静态方法重写。这个其实不能算对象的关系。
覆盖的应用: 当子类需要父类的功能,而功能主体子类有自己特有内容时,可以复写父类中的方法,这样,即沿袭了父类的功能,又定义了子类特有的内容。
之前见一道面试题: 请说一下方法重写和方法重载的区别?
答:方法重写: 就是指子类中的方法与父类中的某一方法具有相同的方法名、返回类型和参数表,则新方法将覆盖原有的方法。
如需父类中原有的方法,可使用super关键字,该关键字引用了当前类的父类。
方法重载则是指:在类中可以创建多个方法,它们具有相同的名字,但具有不同的参数和不同的定义。调用方法时通过传递给它们的不同参数个数和参数类型来决定具体使用哪个方法, 这就是多态性。 重载的时候,方法名要一样,但是参数类型和个数不一样,返回值类型可以相同也可以不相同。无法以返回型别作为重载函数的区分标准。
继承中的构造关系:
子类的构造方法都去访问了父类的无参构造方法:在子类中的构造方法中都有一行默认语句:super()
为什么要这样呢?因为子类会自动具备父类中非私有数据,那么这个数据的初始化靠父类完成。所以会去先调用父类的构造方法对数据进行初始化。
注意:如果父类中没有无参构造方法,那么该怎么办呢?
1、可以通过super(参数)去访问父类中的带参构造方法。
2、可以通过this(参数....)去访问本类中的其他构造方法。 一般不推荐使用。
(7)final关键字
谈到final关键字,大家应该不会太陌生,在使用匿名内部类到时候可能会经常用到,另外Java中的String类也是一个final类。
下面根据自己的理解介绍一下final关键字。
有些时候,有些内容是不能被子类重写的,而我们又知道,只要方法声明为public,相当于子类就可以重写父类的方法,这样就让父类很不安全。
针对这种情况,我们如果能够做一个标记,告诉大家:注意!这个方法是不可以被重写的;那么这个标记是什么呢? final
final 特点:
1、这个关键字是一个修饰符,可以修饰类,成员变量,成员方法。
2、被final修饰的类是一个最终类,不可以被继承。
3、被final修饰的方法是一个最终的方法,不可以被覆盖(即被重写)
4、被final修饰的变量其实就是一个常量,只能复制一次。
具体的情况下面的例子:
package cn.itcast;
class Fu{
//定义了 5个不同的常量来测试
public int Z=10;
public final int X=88;
private final int A=100;
public static final int Y=50;
private static final int B=30;
public final int D; //final空白,必须在初始化对象的时候赋值
public Fu(int x){
D=x;
}
public void f1(){
System.out.println("f1");
}
//无法被子类覆盖的方法
public final void f2(){
System.out.println("f2");
}
public void f3(){
System.out.println("f3");
}
private void f4() {
System.out.println("f4");
}
}
class Zi extends Fu{
public Zi(int x)
{
super(x);
}
//重写父类的f1方法
public void f1() {
System.out.println("父类方法f1被Zi子类重写了!");
}
public void f5() {
//X=20; //报错:The final field Fu.X cannot be assigned
System.out.println(X);
}
}
public class FinalDemo {
public static void main(String[] args) {
Zi z=new Zi(2);
z.f1();
z.f2(); //调用从父类继承过来的final方法
z.f3(); //调用从父类继承过来的方法
// z.f4(); //报错:The method f4() from the type Fu is not visible
z.f5();
z.Z=2; //不被final修饰的常量,其值可以被改变
System.out.println(z.Z);
//以下几种情况,都会报错,因为final的值一旦给定,就无法改变。
// z.X=1; //报错:The final field Fu.X cannot be assigned
// z.A=3; //报错:The field Fu.A is not visible
// z.B=2; //报错:The field Fu.B is not visible
//z.Y=4; //报错:The final field Fu.Y cannot be assigned
//下面这种情况也是错误的,因为D为final空白,依据不同对象值有所不同.
// System.out.println(z.D); //报错:The blank final field D may not have been initialized
}
}
运行结果:
其实这样做的原因就是给一些固定的数据起了个阅读性强的名称。不加final修饰不是也可以使用吗? 那么这个只是一个变量,是可以更改的,加了final,程序更为严谨!
第二讲 多态
多态性是指发送消息给某个对象,让该对象自行决定响应何种行为。
(1)定义:某一类事物的多种存在形态。
即指允许不同类的对象对同一消息做出响应。
即同一消息可以根据发送对象的不同而采用多种不同的行为方式。(发送消息就是函数调用)
例如:视频中老师讲的动物中的猫和狗的例子;
猫这个对象对应的类型是猫类型 猫 x=new 猫();
同时猫也是动物中的一种,也可以把猫称为动物。 动物 y=new 猫();
动物是猫和狗具体事物中抽取出来的父类型。 父类型引用指向了子类对象。
(2)多态的前提:
1、要有继承或者实现关系。
2、要有方法的重写。
3、要有父类引用或者父类接口引用指向子类对象。
(3)多态的弊端:当父类的引用指向子类的对象时,虽然提高了扩展性,但是只能访问父类中具备的方法,不可以访问子类中特有的方法。(前期不能使用后期产生的功能,即访问的局限性)
下面用一个例子来理解一下:
class Animal
{
public void show()
{
System.out.println("show Animal");
}
}
class Cat extends Animal
{
public void show()
{
System.out.println("show Cat");
}
public void playGame()
{
System.out.println("捉迷藏");
}
}
class Dog extends Animal
{
public void show()
{
System.out.println("show Dog");
}
}
class DuoTaiDemo4
{
public static void main(String[] args)
{
//多态
Animal a = new Dog(); //向上转型
a.show();
//给a重新赋值
a = new Cat(); //向上转型
a.show();
Cat c = (Cat)a; //向下转型
c.show();
c.playGame();
Dog d = (Dog)a;
d.show();
}
}
(4)多态的好处:可以提高代码的扩展性和可维护性。
利用多态的好处对上面代码进行优化:
class Animal
{
public void show()
{
System.out.println("show");
}
public void eat()
{
System. out.println("eat");
}
}
class Cat extends Animal
{
public void show()
{
System.out.println("show cat");
}
public void eat()
{
System. out.println("eat 老鼠");
}
}
class Dog extends Animal
{
public void show()
{
System.out.println("show dog");
}
public void eat()
{
System. out.println("eat 骨头");
}
}
class Pig extends Animal
{
public void show()
{
System.out.println("show pig");
}
public void eat()
{
System. out.println("eat 饲料");
}
}
class AnimalTool
{
private AnimalTool(){}
/*
public static void printDog(Dog d)
{
d.show();
d.eat();
}
public static void printCat(Cat c)
{
c.show();
c.eat();
}
public static void printPig(Pig p)
{
p.show();
p.eat();
}
*/
public static void printAnimal(Animal a)
{
a.show();
a.eat();
}
}
class DuoTaiDemo5
{
public static void main(String[] args)
{
//一开始,养了一只狗
Dog d = new Dog();
d.show();
d.eat();
//接着又养了一只狗
Dog d2 = new Dog();
d2.show();
d2.eat();
//继续养狗
Dog d3 = new Dog();
d3.show();
d3.eat();
System.out.println("-----------------------");
//...养了很多只狗,我们就会发现一个问题,养的越多,代码的重复度越高。不好。
//如何改进呢?思考,他们调用的功能,仅仅是由于对象不一样。所以,我们能不能把
//每一个调用的整体封装一下呢,然后传递一个变化的对象即可呢?
//改进版代码
Dog d4 = new Dog();
Dog d5 = new Dog();
Dog d6 = new Dog();
/*
printDog(d4);
printDog(d5);
printDog(d6);
*/
//使用工具类
/*
AnimalTool.printDog(d4);
AnimalTool.printDog(d5);
AnimalTool.printDog(d6);
*/
AnimalTool.printAnimal(d4);
AnimalTool.printAnimal(d5);
AnimalTool.printAnimal(d6);
System.out.println("-----------------------");
//接着,改爱好了,喜欢猫了,那就开始养猫
//基本养猫动作和基本养狗动作一样。
//直接写改进版
Cat c = new Cat();
Cat c2 = new Cat();
Cat c3 = new Cat();
/*
printCat(c);
printCat(c2);
printCat(c3);
*/
//使用工具类
/*
AnimalTool.printCat(c);
AnimalTool.printCat(c2);
AnimalTool.printCat(c3);
*/
AnimalTool.printAnimal(c);
AnimalTool.printAnimal(c2);
AnimalTool.printAnimal(c3);
System.out.println("-----------------------");
//后来,发展了,我喜欢宠物猪。
//怎么办?
//我要创建猪对象,并使用
Pig p = new Pig();
Pig p2 = new Pig();
Pig p3 = new Pig();
/*
printPig(p);
printPig(p2);
printPig(p3);
*/
//使用工具类
/*
AnimalTool.printPig(p);
AnimalTool.printPig(p2);
AnimalTool.printPig(p3);
*/
AnimalTool.printAnimal(p);
AnimalTool.printAnimal(p2);
AnimalTool.printAnimal(p3);
System.out.println("-----------------------");
//到目前为止,有不好的地方。
//比如说:测试类中应该只能有main方法。是不能有其他方法的。
//为了保证不报错,我就得写一下方法。
//重新定义一个工具类,实现输出效果。
//宠物猪过时了,开始改养宠物狼。请问,要实现一样的效果,怎么办?
}
/*
public static void printDog(Dog d)
{
d.show();
d.eat();
}
public static void printCat(Cat c)
{
c.show();
c.eat();
}
public static void printPig(Pig p)
{
p.show();
p.eat();
}
*/
}
Java的这种机制遵循一个原则:当超类对象引用变量引用子类对象时,被引用对象的类型而不是引用对象的类型决定了调用谁的成员方法,但是这个被调用的方法必须是在超类中定义过的,也就是说被子类覆盖的方法。
1、如果a是类A的一个引用,那么,a可以指向类A的一个实例或者说指向类A的一个子类。
2、如果a是接口A的一个引用,那么a必须指向实现了接口A的一个类的实例。
注:如果想用子类对象的特有方法,如何判断对象是哪个具体的子类类型呢?
答: 可以通过一个关键字 instanceof 判断对象是否实现了指定的接口或继承了指定的类。格式: <对象 instanceof 类型> , 判断一个对象是否所属于指定的类型。
Cat instanceof Animal =true ; // Cat 继承了Animal类
/*
需求:
猫,狗。
*/
abstract class Animal
{
abstract void eat();
}
class Cat extends Animal
{
public void eat()
{
System.out.println("吃鱼");
}
public void catchMouse()
{
System.out.println("抓老鼠");
}
}
class Dog extends Animal
{
public void eat()
{
System.out.println("吃骨头");
}
public void kanJia()
{
System.out.println("看家");
}
}
class DuoTaiDemo
{
public static void main(String[] args)
{
function(new Cat());
function(new Dog());
Animal a = new Cat();//向上转型
a.eat();
Cat c = (Cat)a;//向下转型
c.catchMouse();
}
public static void function(Animal a)
{
a.eat();
//用于子类型有限
//或判断所属类型进而使用其特有方法
if(a instanceof Cat)
{
Cat c = (Cat)a;
c.catchMouse();
}
else if(a instanceof Dog)
{
Dog c = (Dog)a;
c.kanJia();
}
}
}
多态在子类中的成员上体现的特点:
1、成员变量:在多态中,子父类成员变量同名。
在编译时期:参考的是引用型变量所属的类中是否有调用的成员。(编译时不产生对象,只检查语法错误)
运行时期:也是参考引用型变量所属的类中是否有调用的成员。
简单一句话:无论编译和运行,成员变量参考的都是引用变量所属的类中的成员变量。
再说的更容易记忆一些:成员变量 ———— 编译运行都看 --------左边
2、成员函数
编译时期:参考引用型变量所属的类中是否有调用的方法。
运行时期:参考的是对象所属的类中是否有调用的方法。
为什么是这样的呢? 因为在子父类中,对于一模一样的成员函数,有一个特性:覆盖。
简单一句话:成员函数,编译看引用型变量所属的类,运行看对象所属的类。
更简单:成员函数————编译看------左边,运行看----右边
3、静态函数
编译时期:参考的是引用型变量所属的类中是否有调用的成员。
运行时期:也是参考引用型变量所属的类中是否有调用的成员。
为什么是这样的呢? 因为静态方法,其实不所属于对象,而是所属于该方法所在的类。
简单说:静态函数————编译运行都看-------右边
第三讲 抽象类
抽象定义:抽象就是从多个事物中将共性的,本质的内容抽取出来。
例如:Java老师和数电老师都属于老师,老师就是抽象出来的概念。
个人理解:抽象即是不具体,在不断抽取过程中,将共性内容中的方法声明抽取,但是方法不一样,没有抽取,这时抽取到的方法并不具体,需要被指定关键字abstract所标示,声明为抽象方法。 抽象方法所在类一定要标示为抽象类,也就是说该类需要被abstract关键字所修饰。
抽象类:Java中可以定义没有方法体的方法,该方法的具体实现由子类完成,该方法称为抽象方法,包含抽象方法的类就是抽象类。
抽象方法的由来:
多个对象都具备相同的功能,但是功能具体内容有所不同,那么在抽取过程中,只抽取了功能定义,并未抽取功能主体,那么只有功能声明,没有功能主体的方法称为抽象方法。
例如:Java老师和数据结构老师都有讲课的方法,但是讲课内容不相同,所以抽取出来的老师虽然都有讲课功能,但是并不明确讲课的细节。
抽象类的特点:
1、抽象类和抽象方法必须用abstract关键字来修饰。
2、抽象方法只有方法声明,没有方法体,定义在抽象类中。
格式:修饰符 abstract 返回值类型 函数名(参数列表);
3、抽象类不可以被实例化,也就是不可以用new创建对象,原因如下:
抽象类是具体事物抽取出来的,本身是不具体的,没有对应实例。例如:老师是一个抽象的概念,真正存在的是数学老师,语文老师。。。
而且,抽象类即使创建了对象,调用抽象方法也没用意义。
4、抽象类通过其子类实例化,而子类需要覆盖掉抽象类中所有的抽象方法后才可以创建对象,否则该子类也是抽象类。
抽象类的成员特点:
1、成员变量 可以是变量,也可以是常量
2、构造方法 有构造方法
3、成员方法 可以是抽象方法,也可以是非抽象方法。
下面是一个具体的例子,帮助理解:
/**
* 一个关于老师的案例:
* 老师:
* Java老师
* 姓名和年龄
* 讲课:Java基础
* 数电老师
* 姓名,年龄
* 讲课:单片机原理
* 由于讲课内容不一样,但是都有讲课的功能。
* 所以我们把讲课抽取出来,定义一个Teacher类。
* 接下来一个Java老师继承,一个数电老师继承即可。
* @author sangyuanyuan
*
*/
abstract class Teacher{
//定义老师具备的属性:姓名 年龄
private String name;
private int age;
//无参构造
public Teacher() {}
//带参构造
public Teacher(String name,int age){
this.name=name;
this.age=age;
}
//创建get和set方法
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public void show(){
System.out.println(name+"***"+age);
}
//抽象方法 讲课
public abstract void teach();
}
class JavaTeacher extends Teacher{
public JavaTeacher() {}
public JavaTeacher(String name,int age){
super(name, age);
}
public void teach() {
System.out.println("Java老师讲解java基础知识!");
}
}
class DataTeacher extends Teacher{
public DataTeacher() {}
public DataTeacher(String name,int age){
super(name, age);
}
//实现父类的抽象方法
public void teach() {
System.out.println("数电老师讲解单片机原理");
}
}
public class AbstractDemo {
public static void main(String[] args) {
//多态 ----Java老师 无参构造
Teacher t=new JavaTeacher();
t.setName("程杰老师");
t.setAge(35);
t.teach();
t.show();
//带参构造
t=new JavaTeacher("程杰老师",35); //调用有参数的构造方法
t.teach();
t.show();
//数电老师 无参构造
t=new DataTeacher();
t.setName("肖勇龙老师");
t.setAge(45);
t.teach();
t.show();
//带参构造
t=new DataTeacher("肖勇龙老师",45); //调用有参数的构造方法
t.teach();
t.show();
}
}
抽象类的几个小问题:
A:抽象类不能被实例化,为什么有构造? 用于子类实例化使用。
B:一个类没有抽象方法,为什么定义为抽象类? 不想被实例化。
C:abstract不能和哪些关键字共存? final private static
D:抽象类中是否有构造函数? 有,用于给子类对象进行初始化。
E:抽象类中是否可以定义非抽象方法? 可以,其实抽象类和一般类没太大的区别,都是在描述事物,只不过抽象类在描述事物时,有些功能不具体。所以抽象类和一般类在定义上都是需要定义属性和行为的。只不过比一般类多了一个抽象函数。而且比一般类少了一个创建对象的部分。
F:抽象类中可不可以不定义抽象方法? 可以,抽象方法目的仅仅为了不让该类创建对象。
第四讲 接口
1、接口概述:如果一个抽象类中的方法都是抽象的,这个时候java就提供了一种更抽象的表达形式:接口。
个人理解: 是一种特殊的抽象类,比抽象类更抽象。因为它里面的方法都是抽象的。
用关键字interface定义, 用implements 实现
2、格式:
interface 接口名{}
class 类名 implement 接口名{}
3、接口中包含的成员最常见的有全局常量、抽象方法。注意:接口中的成员都是固定的修饰符。
A: 成员变量 :只能是常量,默认修饰符 public static final
B:成员方法:只能是抽象方法,默认修饰符 public abstract
推荐:永远手动给出修饰符。
4、接口中有抽象方法,说明接口不可以实例化。接口的子类必须实行了接口中所以的抽象方法后,该子类才可以实例化。否则,该子类还是一个抽象类。
5、接口的特点:
a:接口是对外暴露的规则。(可以理解主板上提供的接口)
b:接口是程序的功能扩展。(就是一个类在继承另一个类的同时还可以实现多个接口)
c:接口的出现降低耦合性。(耦合:类与类的关系 内聚:类自己完成某件事情的能力 低耦合,高内聚)
d:接口可以用来多实现。(接口和类不一样,java将多继承机制通过多实现来体现)
e:类与接口之间是实现关系,而且类可以继承一个类的同时实现多个接口。
f:接口与接口之间可以有继承关系。
6、接口与抽象类的异同:
共性:都是不断抽取出来的抽象的概念。
区别:
1、抽象类体现继承关系,一个类只能单继承,接口体现实现关系,一个类可以多实现。
2、抽象类是继承,是“is a”关系,接口是实现,是“like a”关系。
3、抽象类中可以定义非抽象方法,供子类直接使用,接口的方法都是抽象,接口中的成员都有固定修饰符
4、抽象类的成员修饰符可以自定义,接口中的成员修饰符是固定的,全都是public的。
从其他地方看的,感觉挺好理解的:
在开发之前,先定义规则,A和B分别开发,A负责实现这个规则,B负责使用这个规则。至于A是如何对规则具体实现的,B是不需要知道的。这样这个接口的出现就降低了A和B直接耦合性。
/**
* 一个计算圆的面积和周长的小程序。
* 思路:1、创建一个接口,定义了表示圆周率的常量PI,计算圆面积的方法getArea和计算周长的方getCircumference
* 2、创建一个Cire类来实现CalInterface接口
* 3、最后在主函数中给定一个圆半径,计算其面积和周长
*/
interface CalInterface{
final float PI=3.14f;//定义用于表示圆周率的常量PI
float getArea(float r); //定义一个用于计算面积的方法getArea
float getCircumference(float r);//定义一个用于计算周长的方法
}
class Cire implements CalInterface{
public float getArea(float r) {
float area=PI*r*r; //计算圆面积并赋值给变量area
return area;//返回计算后的圆面积
}
@Override
public float getCircumference(float r) {
float circumference=2*PI*r; //计算圆周长并赋值给变量circumference
return circumference; // 返回计算后的圆周长
}
}
public class InterfaceDemo {
public static void main(String[] args) {
//创建Cire的对象
Cire c=new Cire();
float area=c.getArea(2.0f);
float length=c.getCircumference(3.0f);
System.out.println("半径为:2.0的圆\n圆面积为:"+Float.toString(area)+"\n"+"其周长为:"+Float.toString(length));
}
}