Java语言基础组成:1.关键字 2.标识符 3.注释 4.变量和常量 5.运算符 6.语句 7.函数 8.数组
标识符:由26个英文字母大小写,数字:0-9 符号:- $组成
规则:1.数字不可以开头
2.不可以使用关键字
一. 数据类型:
1.引用数据类型: 包括类(class) 接口(interface) 和数组([ ])
2.基本数据类型:包括字符型(char) 布尔型(boolean) 和数值类型,而数值类型又包括整数类型(byte,short,int,long)和浮点类型(float,double)
3.注意:一般情况下整数默认为int ,小数默认为double
4.各类型所占二进制位数:
基本类型:byte二进制位数:8
包装类:java.lang.Byte
最小值:Byte.MIN_VALUE=-128
最大值:Byte.MAX_VALUE=127
基本类型:short 二进制位数:16
包装类:java.lang.Short
最小值:Short.MIN_VALUE=-32768
最大值:Short.MAX_VALUE=32767
基本类型:int 二进制位数:32
包装类:java.lang.Integer
最小值:Integer.MIN_VALUE=-2147483648
最大值:Integer.MAX_VALUE=2147483647
基本类型:long 二进制位数:64
包装类:java.lang.Long
最小值:Long.MIN_VALUE=-9223372036854775808
最大值:Long.MAX_VALUE=9223372036854775807
基本类型:float 二进制位数:32
包装类:java.lang.Float
最小值:Float.MIN_VALUE=1.4E-45
最大值:Float.MAX_VALUE=3.4028235E38
基本类型:double 二进制位数:64
包装类:java.lang.Double
最小值:Double.MIN_VALUE=4.9E-324
最大值:Double.MAX_VALUE=1.7976931348623157E308
基本类型:char二进制位数:16
包装类:java.lang.Character
最小值:Character.MIN_VALUE=0
最大值:Character.MAX_VALUE=65535
5 单精度和双精度:
单精度:float的精度是6位有效数字,取值范围是10的-38到10的38次方占用4个字节
双精度:15位有数字,范围:10的-308到10的308次方占用8个字节
单精度和双精度的区别:双精度要比单精度所存储的位数更多,至于说3.14是双精度,是因为Java中,默认的小数都是double类型,也就是双精度的,如果要定义单精度的话,那就要在小数的后面加上f或者F,即double=3.14,float=3.14f。
6 关于基本数据类型的一个简单的例子:
1)语句 byte b=3;
编译可以通过,因为3会自动判断是不是在b的范围内。
2)b=b+2
编译会报错 ,3是常量是固定的所以赋值给byte b,而b+2,b是变化的,不确定,当类型提升时会报错:可能会丢失精度。
3)b=(byte)(b+2)
强制类型转换,会把int类型的前3个8位砍掉。强制类型转换的应用,例如:把double转为int。提示:int与float作运算的时候结果为float, ' a'+1中的'a'也会进行自动类型提升结果为98。
7 一些关于计算机理论基础的运算符:
>>代表右移 带符号右移就是将那个数转为2进制然后在前面补1或0
如果是整数补0,负数补1
例:11>>2
0000 0000 0000 0000 0000 0000 0000 1011--->0000 0000 0000 0000 0000 0000 0000 0010
-11>>2
1111 1111 1111 1111 1111 1111 1111 0101---->1111 1111 1111 1111 1111 1111 1111 1101
~代表反码 ;^代表异或 ; &代表与
8 精确运算:
<span style="font-family:SimSun;">double a=0.1;
double b=0.006;
System.out.println(a+b);</span>
打印结果为:0.106000 00001出现这种结果的原因是64位的double不能完全装下运算结果。
如果想得到精确的结果要用到BigDecimal类。
<span style="font-family:SimSun;">BigDecimal a=new BigDecimal("0.1");
BigDecimal b=new BigDecimal("0.006");
a.add(b);</span>
结果为:0.106
9 自动装拆箱
java中数据类型分为两种 : 基本数据类型 引用数据类型(对象)
在 java程序中所有的数据都需要当做对象来处理,针对8种基本数据类型提供了包装类,如下:
int --> Integer
byte --> Byte
short --> Short
long --> Long
char --> Character
double --> Double
float --> Float
boolean --> Boolean
jdk5以前基本数据类型和包装类之间需要互转:
基本---引用 Integer x = new Integer(x);
引用---基本 int num = x.intValue();
1) Integer x = 1; x = x + 1; 经历了什么过程?装箱-->拆箱--> 装箱;
2) 为了优化,虚拟机为包装类提供了缓冲池,Integer池的大小 -128~127 一个字节的大小;
3) String池:Java为了优化字符串操作 提供了一个缓冲池;
二. 面向对象
面向对象是基于面向过程而言的。面向对象和面向过程都是一种思想。
面向过程:强调的是功能行为
面向对象:将功能封装对象,强调具备功能的对象, 面向对象是基于面向过程的。
三 成员变量和局部变量
作用范围:1.成员变量:作用于整个类中。 2.局部变量作用于方法中或者语句中。在内存中的位置:1.成员变量:在堆内存中,因为对象的存在,才在内存中存在。2.局部变量:存在栈内存中
四 封装
封装:是指隐藏对象的属性和实现细节,仅对外提供访问方式。好处:1.将变化隔离 2.便于使用 3.提高重用性 4.提高安全性
封装原则:1.将不需要对外提供的内容都隐藏起来 2.把属性都隐藏起来,提供公共方法对其访问
1 单例设计模式:
解决的问题:保证一个类在内存中的对象唯一性。
实际应用比如:多程序读取一个配置文件时,建议配置文件封装成对象。会方便操作其中数据,又要保证多个程序读到的是同一个配置文件对象,就需要该配置文件对象在内存中是唯一的。Runtime()方法就是单例模式设计出来的。
2 如何保证对象唯一性呢?
思想:
1 不让其他程序创建该类对象。
2 在本类中创建一个本类对象。
3 对外提供方法,让其他程序获取这个对象。
步骤:
1 因为创建对象都需要构造函数初始化,只要将本类中的构造函数私有化,其他程序就无法再创建该类对象;
2 就在类中创建一个本类的对象;
3 定义一个方法,返回该对象,让其他程序可以通过方法就得到本类对象。(作用:可控)
代码体现:
1 私有化构造函数;
2 创建私有并静态的本类对象;
3 定义公有并静态的方法,返回该对象。
示例代码如下:
//饿汉式
class Single {
private Single() {
} // 私有化构造函数。
private static Single s = new Single(); // 创建私有并静态的本类对象。
public static Single getInstance() { // 定义公有并静态的方法,返回该对象。
return s;
}
}
// 懒汉式:延迟加载方式。
class Single2 {
private Single2() {
}
private static Single2 s = null;
public static Single2 getInstance() {
if (s == null)
s = new Single2();
return s;
}
}
五 构造函数:
构造函数中不可以写return语句对象一建立就会调用与之对应的构造函数
构造函数的作用:可以用于给对象进行初始化
构造函数的小细节:当一个类中没有构造函数时,系统会默认给该类加入一个空参数的构造函数。当在类中自定义了构造函数后,默认的构造函数就没有了。
当分析事物时,该事物存在具备一些特性或者行为,那么将这些内容定义在构造函数中。
六 构造函数代码块:
构造代码块中定义的是不同对象共性的初始化内容作用:给对象进行初始化, 对象一建立就运行,且优先于构造函数执行
和构造函数的区别:
1.构造函数代码块是给所有对象进行统一初始化
2.而构造函数是给对应对象初始化,事例代码:
<span style="font-family:SimSun;">class Person{
{
System.out.println("person");//构造代码块
}
}</span>
七 this
this代表它所在函数所属对象的引用。即那个对象在调用this所在的函数,this就代表哪个对象。this的应用:当定义类中功能时,该函数内部要用到调用该函数的对象时,这时用this来表示这个对象。
八 静态
1 用法: 是一个修饰符,用于修饰成员(成员变量,成员函数); 当成员被静态修饰后,就多了一个调用方式,除了可以被对象调用外,还可以直接被类名调用。类名.静态成员。2 static特点:
1) 随着类的加载而加载;也就是说:静态会随着类的消失而消失。说明它的生命周期最长。
2) 优先于对象存在 ; 明确一点:静态是存在,对象是后存在的。
3) 被所有对象所共享。
4) 可以直接被类名调用。
3 实例变量和类变量的区别:
1) 存在位置: 类变量随着类的加载而存在于方法区中。 实例变量随着对象的建立而存在于堆内存中。
2) 生命周期: 类变量生命周期最长,随着类的消失而消失,实例变量生命周期随着对象的消失而消失。
4 静态的使用注意事项:
1) 静态方法只能访问静态成员; 非静态方法既可以访问静态也可以访问非静态。
2) 静态方法中不可以定义this,super关键字; 因为静态优先于对象存在。所以静态方法中不可以出现this。
5 静态的利弊:
利:对对象的共享数据进行单独空间的存储,节省空间。没有必要每一个对象中都存储一份。可以直接被类名 调用
弊:生命周期过长。访问有局限(静态虽好,但只能静态访问静态)
九.多态
1 多态的特点:
1)多态的体现:
父类的引用指向了自己的子类对象。 // Animal a = new Cat();
父类的引用也可以接受自己的子类对象。
2)多态的前提:
必须是类与类之间有关系,要么继承要么实现。
通常还有一个前提:存在覆盖。
3)多态的好处:
多态的出现大大的提高了程序的扩展性。
4)多态的弊端:
提高了扩展性但是只能使用父类的引用访问父类中成员,即不能访问子类成员。 ( 前期不能使用后期产生的功能,即访问的局限性 )
5)转型:
向上转型:类型提升。
向下转型:强制将父类的引用转换成子类类型,向下转型。
6)注意:
千万不能出现这样的操作:就是将父类对象转成子类类型。
多态自始至终都是子类对象在做着变化。
2 在多态中非静态成员函数的特点:
在编译时期:参阅引用类型变量所属的类中是否有调用的方法,如果有,编译通过。反之编译失败。
在运行时期:参阅对象所属的类中是否有调用的方法。
简单总结:成员函数在多态调用时,编译看左边,运行看右边。
3 在多态中静态成员函数的特点:
多态中成员变量的特点:无论编译还是运行,都参考左边(引用型变量所属的类)。
多态中静态成员函数的特点:无论编译和运行,都参考左边。
静态绑定:静态方法一加载内存就被绑定在方法所属的类上。
class Father {
static String sayHi() {
return "Father say hi";
}
String name() {
return "name is Father";
}
}
class Son extends Father {
static String sayHi() {
return "Son say hi";
}
String name() {
return "name is Son";
}
}
class Differ {
public static void main(String args[]) {
Father s = new Son();
System.out.println(s.sayHi() + "----" + s.name());
}
}
class Father {
static String sayHi() {
return "Father say hi";
}
String name() {
return "name is Father";
}
}
class Son extends Father {
static String sayHi() {
return "Son say hi";
}
String name() {
return "name is Son";
}
}
class Differ {
public static void main(String args[]) {
Father s = new Son();
System.out.println(s.sayHi() + "----" + s.name());
}
}
输出结果为:
Father say hi----name is Son
十 继承:
1 继承的好处:
1) 提高了代码的复用性
2) 让类与类之间产生了关系。有这个关系,才有了多态的特性。
注意:不能为了获取其他类的方法,简化代码而继承。
原因:多继承容易带来安全隐患:当多个父类中定义了相同的功能,当功能内容不同时子类对象不确定要运行哪一个(致命方块)。但是java保留这种机制。并用另一种体现形式来完全表现。多实现。
4) 子类出现后,类成员的特点:
变量
如果子类中出现非私有的同名成员变量时,子类要访问本类中的变量,用this。子类要访问父类中的同名变量时 用super。
super的使用和this的使用几乎一致。
this代表的是本类对象的引用
super代表的是父类对象的引用
子父类中的函数
当子类出现和父类一模一样的函数时,当子类对象调用该函数,会运行子类函数的内容。如同父类的函数被覆盖一样。
这种情况是函数的另一种特性:重写(覆盖)
当子类继承父类,沿袭了父类的功能到子类中,但子类虽具备该功能,但功能的内容却和父类不一致。这时,没有必要定义新功能,而是使用覆盖特性,保留父类的功能定义,并重写功能内容。
提示:1.子类覆盖父类,必须保证子类权限大于等于父类权限,才可以覆盖否则编译失败。
2.静态只能覆盖静态。静态不能覆盖非静态。
3.重载和重写的区别:重载:只看同名函数的参数列表, 重写:字父类方法一模一样。(子类的返回类型可与父类有一定关系)。
子父类中的构造函数
在对子类进行初始化时,父类的构造函数也会运行,那是因为子类的构造函数默认第一行有一条隐式的语句:super(); super():会访问父类中空参数的构造函数。而且子类中的所有构造函数默认第一行是super();
问:为什么子类一定要访问父类中的构造函数。
答:因为父类的数据子类可以直接获取。所以建立时,需要先查看父类但是如何对这些数据进行初始化的。所以子类在对象初始化时,要先访问一下父类中 的构造函数。如果要访问父类中指定的构造函数,可以通过动定义super语句的方式指定。
注意:super语句一定定义在子类构造函数的第一行,构造函数内要么有this()要么有super()两者只能择其一。
结论:子类的所有构造函数,默认都会访问父类中空参数的构造函数。因为子类每一个构造函数内的第一行都有一句隐式语句super();当父类中没有空参数构造函数时,子类必须通过super或者this语句形式来指定要访问的构造函数。当然子类的构造函数第一行也可以收到指定this语句来访问本类中的构造函数,子类中至少有一个构造函数会访问父类中的构造函数。
提示:抽象函数中也有构造方法但不能被实例化,子类的构造函数会调用抽象父类的构造函数。并且java中子类不不继承父类的构造函数的。
2 final特点:
1)这个关键字是一个修饰符,可以修饰类,方法,变量。
2)被final修饰的类是一个最终类,不可以被继承。
3)被final修饰的方法是一个最终方法,不可以被覆盖。
4)被final修饰的变量是一个常量,只能赋值一次。
3 抽象类
当多个类出现相同功能,但是功能主体不同,这时可以进行向上抽取功能定义,而不抽取功能主体。
抽象类的特点:
1)抽象方法一定在抽象类中,抽象方法只能定义在抽象类中,抽象类和抽象方法必须由abstract关键字修饰(可以描述类和方法,不可以描述变量)。
2)抽象方法只定义方法声明,并不定义方法实现。
3)抽象类不可以被创建对象(实例化),因为调用抽象方法没有意义。4)抽象类中的抽象方法要被使用,必须用子类复写所有的抽象方法后,建立子类对象调用。如果子类只覆盖了部分方法,那么该子类好是一个抽象类。
5)抽象类只能单继承。
抽象类的细节:
1)问:抽象类中是否有构造函数?答:有,用于给子类对象进行初始化。
2)问:抽象类中是否可以定义非抽象方法?答:可以。其实,抽象类和一般类没有太大的区别,都是在描述事物,只不过抽象类在描述事物时,有些功能不具体。所以抽象类和一般类在定义上,都是需要定义属性和行为的。只不过,比一般类多了一个抽象函数。而且比一般类少了一个创建对象的部分。
3)抽象关键字abstract和哪些不可以共存?final , private , static
4)抽象类中可不可以不定义抽象方法?可以。抽象方法目的仅仅为了不让该类创建对象。
4 接口
1) 接口:初期理解,可以认为是一个特殊的抽象类。当抽象类中的方法都是抽象的,那么该类可以通过接口的形式表示。class定义类,interfce用于定义接口。接口是不可以创建对象的,因为有抽象方法。需要被子类实现,子类对接口中的对象方法全都覆盖后,子类才可以实例化,否则子类是一个抽象类。接口可以被类多实现,也是对多继承不支持的转换形式。java支持多实现。接口和接口间可以继承,并支持多继承。2) 接口定义时,格式特点:
接口中的常见定义:常量,抽象方法。
接口中的成员都有固定修饰符:
常量:public static final
方法:public abstract
3) 接口的特点:
接口是对外暴露规则
接口是程序的功能扩展
接口可以用来多实现
类与接口之间是实现关系,而且类可以继承一个类的同时实现多个接口。
接口与接口之间可以有继承关系
下面是一个模拟计算机主板运行与声卡网卡之间的关系,体现了接口提高了功能扩展,降低了耦合性的代码:
public interface PCI {
public void open();
public void close();
}
class MainBoard {
public void run() {
System.out.println("mainboard run");
}
public void usePCI(PCI p) {
if (p != null) {
p.close();
p.open();
}
}
}
class NetCard implements PCI {
@Override
public void open() {
// TODO Auto-generated method stub
System.out.println("netcard open");
}
@Override
public void close() {
// TODO Auto-generated method stub
System.out.println("netcaed close");
}
}
class SoundCard implements PCI {
@Override
public void open() {
// TODO Auto-generated method stub
System.out.println("SoundCard open");
}
@Override
public void close() {
// TODO Auto-generated method stub
System.out.println("SoundCard close");
}
}
class DuoTaiDemo {
public static void main(String args[]) {
MainBoard mb = new MainBoard();
mb.run();
mb.usePCI(new NetCard());
mb.usePCI(new SoundCard());
}
}
打印结果为:
mainboard run
netcaed close
netcard open
SoundCard close
SoundCard open
4) 抽象类和接口的区别:
(1) 抽象类只能被继承,而且只能单继承。
接口需要被实现,而且可以多实现。
(2) 抽象类中可以定义非抽象方法,子类可以直接继承使用。
接口中都有抽象方法,需要子类去实现。
(3) 抽象类使用的是is a 关系。
接口使用的is like a 关系。
我的理解:is a和is like a都是用于继承,区分的关键在于是否实现了新的接口,比如A继承B,但是A只是覆盖了B的方法,则称A is a B;如果A中新增了方法,则称A is like a B.
(4) 抽象类的成员修饰符可以自定义。
接口中的成员修饰符是固定的。全都是public的。
十二.内部类
1 内部类的访问规则:1) 内部类可以直接访问外部类的成员,包括私有。之所以可以直接访问外部类中的成员,是因为内部类中持有一个外部类的引用。格式:外部类.this
2) 外部类要访问内部类,必须建立内部类对象,访问格式:当内部类定义在外部的成员位置上,而且非私有,可以在外部其他类中可以直接建立内部类对象。格式:外部类名。内部类名 变量名=外部类对象.内部类对象;
3) 当内部类在成员位置上,就可以被成员修饰符所修饰。比如,private::将内部类在外部类中进行封装;static:内部类就具备static特性。当内部类被static修饰后,只能直接访问外部类中的static成员,出现了访问局限。
4) 在外部其他类中,如何直接访问static内部类的非静态成员: new Outer.Inner().function();
5) 在外部其他类中,如何直接访问static内部类的静态成员:Outer.Inner().funtion();
提示:当内部类中定义了静态成员,该内部类必须是static的。当外部类中的静态方法访问内部类时,内部类也必须是static的。
2 内部类定义在局部时:
1)不可以被成员修饰符修饰(static.....)
2)可以直接访问外部类中的成员,因为还持有外部类的引用。但是不可以访问它所在的局部中的变量,只能访问被final修饰的局部变量。(局部变量的生命周期比内部类短因此加final可延长其生命周期) 可参考以下代码:
class Outer {
int x = 3;
void method(final int a) {
final int y = 4;
class Inner {
void function() {
System.out.println(a);
}
}
new Inner().function();
}
}
class InnerClassDemo {
public static void main(String args[]) {
Outer out = new Outer();
out.method(6);
out.method(7);
}
}
打印结果为:6
7
3 匿名内部类:
1)匿名内部类:没有名字的内部类。就是内部类的简化形式。一般只用一次就可以用这种形式。匿名内部类其实就是一个匿名子类对象。想要定义匿名内部类:需要前提,内部类必须继承一个类或者实现接口。
2)定义匿名内部类的前提:内部类必须是继承一个类或者实现接口。
3)匿名内部类的格式:new 父类或者接口(){ //定义子类成员或者覆盖父类方法 }
4)其实匿名内部类是一个匿名子类对象。而且这个对象有点”胖“。可以理解为带内容的对象。
5)匿名内部类中定义的方法最好不超过3个。
匿名对象使用方式一:当对象的方法只调用一次时,可以用匿名对象来完成,这样写比较简化。如果对一个对象进行多个成员调用,必须给这个对象起个名字。
匿名对象使用方式二:可以将匿名对象作为实际参数进行传递。 当函数的参数是接口类型引用时,如果接口中的方法不超过3个。可以通过匿名内部类来完成参数的传递。 其实就是在创建匿名内部类时,该类中的封装的方法不要过多,最好两个或者两个以内。