java核心技术学习笔记

byte,short,char ->int->long->float->double   由大到小需要强制转换


基本数据类型(或叫原生类,内置类型)
    整数:byte,short,int long
    浮点类型:float double
    字符类型:char
    布尔类型:boolean
引用类型:数组,类,接口

重写 总结: 
1.发生在父类与子类之间 
2.方法名,参数列表,返回类型(除过子类中方法的返回类型是父类中返回类型的子类)必须相同 
3.访问修饰符的限制一定要大于被重写方法的访问修饰符(public>protected>default>private) 
4.重写方法一定不能抛出新的检查异常或者比被重写方法申明更加宽泛的检查型异常

重载 总结: 
1.重载Overload是一个类中多态性的一种表现 
2.重载要求同名方法的参数列表不同(参数类型,参数个数甚至是参数顺序) 
3.重载的时候,返回值类型可以相同也可以不相同。无法以返回型别作为重载函数的区分标准

所有的异常都直接继承于 java.lang.Exception

resultSet数组是索引从1开始的

在结构化的程序设计中,模块划分原则是 --> 模块内具有高聚合度,模块间具有低耦合度


char[] ch = new char[2];//ch[0]输出 默认空格
int[] Int = new int[2];//Int[0]输出 0
String[] str = new String[2];//str[0]输出null

假设i=0
    i=i++的结果是0 因为是先算i=i 后计算++;
    i=++i的结果是1,因为先算++i  再算=
-----------------------------------------------------------------------------------------------
一般关系数据模型和对象数据模型之间有以下对应关系:表对应类,记录对应对象,表的字段对应类的属性

关键字 this 有两个用途 : 
    一是引用隐式参数, 
    二是调用该类其他的构造器,


super关键字也有两个用途 : 
    一是调用超类的方法, 
    二是调用超类的构造器 。在调用构造器的时候 , 
这两个关键字的使用方式很相似 。 调用构造器的语句只能作为另一个构造器的第一条语句出现 。 构造参数既可以传递给本类 ( this ) 的其他构造器 , 也可以传递给超类 ( super ) 的构造器 。

final关键字

final 修饰的class 不能extend;修饰的method,不能override; 修饰的variable,不能update
final 修饰的变量有三种 静态变量 实例变量 局部变量

接口需要先继承extends 再 implements  不然会编译报错

compareTo 方法比较两个字符串, 如果字符串相同则返回 0;如果按照字典顺序,第一个字符串比第二个字符串靠前,就返回负值,否则,返回正值
----------------------------------------------------------------------------------------------
面向对象的五大基本原则:
    单一职责原则(SRP)--> 一个类或者模块应该有且只有一个改变的原因
    开发封闭原则(OCP)--> 对于扩展是开放的,对于修改是关闭的
    里氏替换原则(LSP)--> OCP作为OO的高层原则,主张使用“抽象(Abstraction)”和“多态(Polymorphism)”将设计中的静态结构改为动态结构,维持设计的封闭性。“抽象”是语言提供的功能。“多态”由继承语义实现。
    依赖倒转原则(DIP)--> 程序要依赖于抽象接口,不要依赖于具体实现。简单的说就是要求对抽象进行编程,不要对实现进行编程,这样就降低了客户与实现模块间的耦合。
    接口隔离原则(ISP)--> 客户端不应该依赖它不需要的接口,一个类对另一个类的依赖应该建立在最小的接口上

------------------------------------- abstract -----------------------------------------------
抽象类方法不加修饰符情况下访问权限默认是default
许多程序员认为, 在抽象类中不能包含具体方法 。 建议尽量将通用的域和方法 ( 不管是否是抽象的 ) 放在超类 ( 不管是否是抽象类 ) 中 。
抽象类最好是祖先类, 
    a)    允许有抽象方法;  //接口中方法都为抽象方法。
    b)    抽象类不能直接创建对象(- 即 new 对象() -),只能用作--派生子类--,实现其中的抽象方法; 
    c)    抽象类可以实现接口,不一定重写所有的抽象方法 abstract class 类名 implements 接口1,接口2...{};
    d)    抽象类也可以继承具体类;
抽象类可以包括抽象方法和非抽象方法;如果类里面有抽象方法,那么类一定要声明为抽象的

public abstract class Person {
    public abstract String getDescription();
}
public class Student extends Person{
    public String getDescription() { return "Student"; }
}

public class Test {
    public static void main(String[] args) {  Person stu = new Student();  }
}
------------------------------------------------------------------------------------------
Java 用于控制可见性的 4 个访问修饰符
    1 ) 仅对本类可见 private 
    2 ) 对所有类可见 public 
    3 ) 对本包和所有子类可见 protected
    4 ) 对本包可见 — 默认 ( 很遗憾 ) ,不需要修饰符
 
Java 语言规范要求 equals 方法具有下面的特性 :
    1 ) 自反性 : 对于任何非空引用 x , x.equals ( x ) 应该返回 true
    2 ) 对称性 : 对于任何引用 x 和 y , 当且仅当 y.equals ( x ) 返回 true , x.equals ( y ) 也应该返回 true
    3 ) 传递性 : 对于任何引用 x 、 y 和 z , 如果 x.equals ( y ) 返回 true, y.equals ( z ) 返回 true ,x.equals ( z ) 也应该返回 true 
    4 ) 一致性 : 如果 x 和 y 引用的对象没有发生变化, 反复调用 x.equals ( y ) 应该返回同样的结果
    5 ) 对于任意非空引用 x , x.equals ( null ) 应该返回 false 

编写一个完美的 equals 方法的建议 :
1 ) 显式参数命名为 otherObject , 稍后需要将它转换成另一个叫做 other 的变量 。
2 ) 检测 this 与 otherObject 是否引用同一个对象 :
    if ( this = otherObject ) return true ;
    这条语句只是一个优化。 实际上 , 这是一种经常采用的形式 。 因为计算这个等式要比一个一个地比较类中的域所付出的代价小得多 。
3 ) 检测 otherObject 是否为 null , 如 果 为 null , 返 回 false 。 这项检测是很必要的 。
    if ( otherObject = null ) return false ;
4 ) 比较 this 与 otherObject 是否属于同一个类。 如果 equals的语义在每个子类中有所改变, 就使用 getClass检测 :
    if ( getClass ( ) ! = otherObject . getCIassO ) return false ;
    如果所有的子类都拥有统一的语义, 就使用instanceof 检测 :
    if ( ! ( otherObject instanceof ClassName ) ) return false ;
5 ) 将 otherObject 转换为相应的类类型变量 :
    ClassName other = ( ClassName ) otherObject
6 ) 现在开始对所有需要比较的域进行比较了 。 使用 = 比较基本类型域 , 使用 equals 比较对象域。 如果所有的域都匹配 , 就返回true ; 否 则 返 回 false 。
    return fieldl = = other . field
    & & Objects . equa 1 s ( fie 1 d 2 , other . field 2 )
    如果在子类中重新定义 equals , 就要在其中包含调用 super . equals ( other ) 。

散列码(hash code ) 是由对象导出的一个整型值。 散列码是没有规律的 。 如果x和y是两个不同的对象 , x.hashCode ( ) 与 y.hashCode ( ) 基本上不会相同 。    
如果其参数为null,返回NullPointerException;参数为“ ” 这个方法会返回0 , 否则返回对参数调用 hashCode 的结果。
Equals 与 hashCode 的定义必须一致 : 如果 x . equals ( y ) 返回 true , 那么 x . hashCode ( ) 就必须与 y . hashCode ( ) 具有相同的值


setAccessible 方法是 AccessibleObject 类中的一个方法 , 它是 Field 、 Method 和 Constructor类的公共超类
void setAccessible ( boolean flag )为反射对象设置可访问标志,flag为 true 表明屏蔽 Java 语言的访问检查, 使得对象的私有属性也可以被査询和设置
boolean isAccessible ( )返回反射对象的可访问标志的值
static void setAccessible ( AccessibleObject [ ] array , boolean flag )是一种设置对象数组可访问标志的快捷方法

----------------------------------  继承的设计技巧 ------------------------------------------------------------
1 . 将公共操作和域放在超类
    这就是为什么将姓名域放在 Person 类中, 而没有将它放在 Employee和 Student 类中的原因。

2 . 不要使用受保护的域
    有些程序员认为, 将大多数的实例域定义为 protected 是一个不错的主意 , 只有这样 , 子类才能够在需要的时候直接访问它们。 然而 , protected机制并不能够带来更好的保护, 其原因主要有两点。 第一 , 子类集合是无限制的 , 任何一个人都能够由某个类派生一个子类 , 并编写代码以直接访问 protected 的实例域, 从而破坏了封装性 。 第二 ,在 Java 程序设计语言中, 在同一个包中的所有类都可以访问 proteced 域 , 而不管它是否为这个类的子类 。不过, protected 方法对于指示那些不提供一般用途而应在子类中重新定义的方法很有用 。

3 . 使用继承实现 “ is - a ” 关系
    使用继承很容易达到节省代码的目的, 但有时候也被人们滥用了 。 例如 , 假设需要定义一个钟点工类。 钟点工的信息包含姓名和雇佣日期 , 但是没有薪水 。 他们按小时计薪 , 并且不会因为拖延时间而获得加薪 - 这似乎在诱导人们由 Employee 派生出子类 Contractor , 然后再增加一个 hourly Wage 域。
    public class Contractor extends Employee{    private double hourlyWage ;}
    这并不是一个好主意。 因为这样一来 , 每个钟点工对象中都包含了薪水和计时工资这两个域 。在实现打印支票或税单方法耐候, 会带来诞的麻烦 , 并且与不采用继承 , 会多写很多代码 。钟点工与雇员之间不属于 “ is - a ” 关系 。 钟点工不是特殊的雇员。

4 . 除非所有继承的方法都有意义, 否则不要使用继承
    假设想编写一个 Holiday 类 3 毫无疑问, 每个假日也是一日 , 并且一日可以用Gregorian,Calendar 类的实例表示, 因此可以使用继承 。
    class Holiday extends CregorianCalendar { . . , }
    很遗憾 , 在继承的操作中, 假日集不是封闭的 。 在 GregorianCalendar 中有一个公有方法add , 可以将假日转换成非假日 :Holiday Christmas ;
    Christmas . add ( Calendar . DAY _ OF _ MONTH , 12 ) ;因此, 继承对于这个例子来说并不太适宜 。需要指出 , 如果扩展 LocalDate 就不会出现这个问题。 由于这个类是不可变的 , 所以没有任何方法会把假日变成非假日 。

5 . 在覆盖方法时, 不要改变预期的行为
    置换原则不仅应用于语法 , 而且也可以应用于行为, 这似乎更加重要 。 在覆盖一个方法的时候, 不应该毫无原由地改变行为的内涵 。 就这一点而言 , 编译器不会提供任何帮助 , 即编译器不会检查重新定义的方法是否有意义。 例如 , 可以重定义 Holiday 类中 add 方法 “ 修正 ” 原方法的问题, 或什么也不做 , 或抛出一个异常 , 或继续到下一个假日 。然而这些都违反了置换原则 。 语句序列
    int dl = x . get ( Calendar . DAY _ OF _ MONTH ) ;
    x . add ( Calendar . DAY _ OF _ MONTH , 1 ) ;
    int d 2 = x . get ( Calendar . DAY _ OF _ HONTH ) ;
    System . out . println ( d 2 - dl ) ;
    不管 x 属于 GregorianCalendar 类 , 还是属于 Holiday 类, 执行上述语句后都应该得到预期的行为。当然 , 这样可能会引起某些争议。 人们可能就预期行为的含义争论不休 。 例如 ,有些人争论说, 置换原则要求 Manager . equals 不处理 bonus 域 , 因为 Employee . equals 没有它 。 实际上, 凭空讨论这些问题毫无意义 。 关键在于 , 在覆盖子类中的方法时 , 不要偏离最初的设计想法。

6 . 使用多态 , 而非类型
    信息无论什么时候, 对于下面这种形式的代码
    if ( x is of type 1 )
    actioniM \
    else if ( x is of type 2 )
    actioniM ;
    都应该考虑使用多态性。action , 与 3 如 0112 表示的是相同的概念吗? 如果是相同的概念, 就应该为这个概念定义一个方法 , 并将其放置在两个类的超类或接口中, 然后 , 就可以调用X . action 0 \以便使用多态性提供的动态分派机制执行相应的动作。使用多态方法或接口编写的代码比使用对多种类型进行检测的代码更加易于维护和扩展。

7 . 不要过多地使用反射
    反射机制使得人们可以通过在运行时查看域和方法, 让人们编写出更具有通用性的程序 。这种功能对于编写系统程序来说极其实用, 但是通常不适于编写应用程序 。 反射是很脆弱的 ,即编译器很难帮助人们发现程序中的错误 , 因此只有在运行时才发现错误并导致异常。


-----------------------------------------------  interface ------------------------------------------------------------
在接口中的所有方法都自动地是 public。 不过 , 在实现接口时 ,必须把方法声明为 pubHc ; 否则 , 编译器将认为这个方法的访问属性是包可见性 , 即类的默认访问属性, 之后编译器就会给出试图提供更严格的访问权限的警告信息
 x< y 时, Double . compare ( x , y )调用会返回 - 1 ; 如果 x > y 则返回 1

 //对集合内的对象按照要求进行一个排序
  Collections.sort(list,new Comparator() {
        @Override
        public int compare(Student o1, Student o2) { return o1.age-o2.age; }//按照年龄排序
 });

-------------------------------------------------lamdal-------------------------------------------------------------------

::双冒号运算就是Java中的[方法引用],[方法引用]的格式是-->   类名::方法名 
注意是方法名哦,后面没有括号“()”哒。为啥不要括号,因为这样的是式子并不代表一定会调用这个方法。这种式子一般是用作Lambda表达式,Lambda有所谓懒加载嘛,不要括号就是说,看情况调用方法。 
例如
表达式: person -> person.getAge(); 可以替换成 Person::getAge
表达式 () -> new HashMap<>(); 可以替换成 HashMap::new


1.替代匿名内部类
  @Test
    public void oldRunable() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("The old runable now is using!");
            }
        }).start();
    }

     @Test
    public void runable() {
        new Thread(() -> System.out.println("It's a lambda function!")).start();
    }

2.使用lambda表达式对集合进行迭代
     List languages = Arrays.asList("java","scala","python");
    //before java8
    for(String each:languages) { System.out.println(each); }
    //after java8
    languages.forEach(x -> System.out.println(x));
    languages.forEach(System.out::println);    


3.用lambda表达式实现map
    @Test
    public void mapTest() {
        List cost = Arrays.asList(10.0, 20.0,30.0);
        cost.stream().map(x -> x + x*0.05).forEach(x -> System.out.println(x));
    }
    最后的输出结果:
    10.5
    21.0
    31.5
4.用lambda表达式实现map与reduce
     @Test
    public void mapReduceTest() {
        List cost = Arrays.asList(10.0, 20.0,30.0);
        double allCost = cost.stream().map(x -> x+x*0.05).reduce((sum,x) -> sum + x).get();
        System.out.println(allCost);//63.0
    }

5.filter操作
  @Test
    public void filterTest() {
        List cost = Arrays.asList(10.0, 20.0,30.0,40.0);
        List filteredCost = cost.stream().filter(x -> x > 25.0).collect(Collectors.toList());
        filteredCost.forEach(x -> System.out.println(x));//30    40
    }

 ---------------------------------------  inner_class -------------------------------------------------- 
有时候 , 使用内部类只是为了把一个类隐藏在另外一个类的内部, 并不需要内部类引用外围类对象。 为此 , 可以将内部类声明为static , 以便取消产生的引用。
 内部类要获取外部类private参数,外部类.this.参数
 静态嵌套类不能访问外部类的实例变量和方法
 注意: 
    1.非静态内部类中不允许定义静态成员 
    2.外部类的静态成员不可以直接使用非静态内部类 
    3.静态内部类,不能访问外部类的实例成员,只能访问外部类的类成员
内部类 ( inner_class ) 是定义在另一个类中的类。 为什么需要使用内部类呢 ?其主要原因有以下三点 :
    •内部类方法可以访问该类定义所在的作用域中的数据, 包括私有的数据
    •内部类可以对同一个包中的其他类隐藏起来
    •当想要定义一个回调函数且不想编写大量代码时 , 使用匿名( anonymous ) 内部类比较便捷
      
第一种形式的普通匿名内部类,重写声明类型中对应的函数。
class Popcorn {  public void pop(){  System.out.println("pop"); } }
public class Food{ 
    public static void main(String[] args) {
        Popcorn p=new Popcorn(){
            public void pop() { System.out.println("test"); }//这是建立匿名内部类的关键,重写父类的一个或多个方法。
        };
        p.pop();运行结果--test
    }
}注意:Popcorn引用变量不是Popcorn实例,而是Popcorn匿名(未命名)子类的实例。运行结果--test

第二种接口声明,赋值时实现。
interface Cookable { public void cook(); }
public class Food1 {
    public static void main(String[] args) {
        Cookable c=new Cookable() {
            public void cook() { System.out.println("test"); }
        };
        c.cook();运行结果--test
    }
}注意:匿名实现类只能实现一个接口。不能同时继承一个类和实现一个接口。

参数定义的匿名内部类
public class MyWonderfulClass {
    public static void main(String[] args) {
        new MyWonderfulClass().go();
    }
    void go() {
        new Bar().dostaff( new Fool() {
            @Override
            public void fool() {
                System.out.println( "fool" );
            }
        } );
    }

interface Fool { void fool(); }
class Bar {  void dostaff(Fool f) { f.fool();   } }

--------------------------------------日志---------------------------
为一个简单的应用程序 , 选择一个日志记录器, 并把日志记录器命名为与主应用程序包一样的名字
例如 : com.mycompany.myprog ,
这是一种好的编程习惯 。 另外, 可以通过调用下列方法得到日志记录器
    Logger logger = Logger . getLogger ( " com . mycompany . myprog " ) ;
为了方便起见, 可能希望利用一些日志操作将下面的静态域添加到类中 :
    private static final Logger logger = Logger . getLogger ( " com . mycompany . nyprog " ) 

所有级别为INFO 、WARNING和SEVERE的消息都将显示到控制台上。 因此,最好只将对程序用户有意义的消息设置为这几个级别。将程序员想要的日志记录设定为FINE,是一个很好的选择 

-------------------------------------泛型-------------------------------------------

类型变量使用大写形式, 且比较短 ,这是很常见的 。 在 Java 库中 , 使用变量 E 表示集合的元素类型 , K 和 V 分别表示表的关键字与值的类型 。 T ( 需要时还可以用临近的字母 U 和 S ) 表示 “ 任意类型 ”

在此为什么使用关键字extends 而不是 implements ? 毕竟 ,Comparable 是一个接口 。 下面的记法
< T extends BoundingType 〉
表示 T 应该是绑定类型的子类型 ( subtype ) 。 T 和绑定类型可以是类, 也可以是接口 。 选择关键字 extends 的原因是更接近子类的概念, 并且 Java的设计者也不打算在语言中再添加一个新的关键字 ( 如 sub )。
一个类型变量或通配符可以有多个限定 , 例如 :
T extends Comparable & Serializable
限定类型用 “ & ” 分隔,而逗号用来分隔类型变量 。
在 Java 的继承中 , 可以根据需要拥有多个接口超类型 ,但限定中至多有一个类。 如果用一个类作为限定,它必须是限定列表中的第一个 

需要记住有关Java 泛型转换的事实 :
• 虚拟机中没有泛型 , 只有普通的类和方法 。
• 所有的类型参数都用它们的限定类型替换 。
• 桥方法被合成来保持多态 。
• 为保持类型安全性 , 必要时插人强制类型转换

 带有超类型限定的通配符“?”可以向泛型对象写人 , 带有子类型限定的通配符可以从泛型对象读取
int compareTo ( ? super T )
有可能被声明为使用类型 T 的对象 ,也有可能使用 T 的超类型( 如当 T 是 LocalDate , T的一个子类型 )。无论如何 ,传递一个T 类型的对象给 compareTo 方法都是安全的

--------------------------------------Collection-------------------
集合类型             描 述
ArrayList 一种可以动态增长和缩减的索引序列
LinkedList 一种可以在任何位置进行高效地插人和删除操作的有序序列
ArrayDeque 一种用循环数组实现的双端队列
HashSet 一种没有重复元素的无序集合
TreeSet —种有序集
EnumSet 一种包含枚举类型值的集
LinkedHashSet 一种可以记住元素插人次序的集
PriorityQueue 一种允许高效删除最小元素的集合
HashMap 一种存储键 / 值关联的数据结构
TreeMap — 种键值有序排列的映射表
EnumMap — 一种键值属于枚举类型的映射表
LinkedHashMap 一种可以记住键 / 值项添加次序的映射表
WeakHashMap 一种其值无用武之地后可以被垃圾回收器回收的映射表
IdentityHashMap 一种用 = 而不是用 equals 比较键值的映射表


如果需要把一个数组转换为集合, Arrays . asList 包装器可以达到这个目的 。 例如 :
    String[] values = . .HashSet < String > staff = new HashSeto( Arrays.asList(values)) ;
从集合得到数组会更困难一些。 当然 , 可以使用 toArray 方法 :
    Object[] values = staff.toArray() ;
不过, 这样做的结果是一个对象数组 。 尽管你知道集合中包含一个特定类型的对象 , 但不能使用强制类型转换 :
    String[] values = ( String[]] ) staff.toArray() ; // Error !
toArray 方法返回的数组是一个 Object[] 数组 ,不能改变它的类型。实际上 ,必须使用 toArray 方法的一个变体形式, 提供一个所需类型而且长度为 0 的数组 。 这样一来 , 返回的数组就会创建为相同的数组类型 
    String[] values = staff.toArray ( new String[0] ) 

------------------------------------------------------ 并发 --------------------------------------------------------------------
interrupted和 islnterrupted。 Interrupted方法是一个静态方法, 它检测当前的线程是否被中断 。而且 ,调用 interrupted 方法会清除该线程的中断状态。 另一方面 , islnterrupted方法是一个实例方法, 可用来检验是否有线程被中断 。 调用这个方法不会改变中断状态

重写Thread类的run方法和实现Runnable接口的run方法还有一个很重要的不同:
    Thread类的run方法是不能共享的,也就是A线程不能把B线程的方法当作自己的执行单元,而使用Runnable接口则可以实现这一点,使用同一个Runnable的实例构造不同的Thread实例

守护线程经常用作与执行一些后台任务,因此有时它也被称为后台线程,当你希望关闭某些线程的时候,或者退出JVM进程的时候,一些线程能够自动关闭,此时就可以考虑用守护线程完成工作,setDaemon(true);isDaemon方法判断线程是否为守护线程

TimeUtil可以完成替代sleep,功能更加强大

sleep会使线程短暂block,会在给定时间内释放CPU资源
yield会是RUNNING状态进入RUNNABLE状态(如果CUP调度没有忽略这个提示的话)

线程默认的优先级和它的父类保持一致--都是5

public long getId()获取线程id,在整个JVM进程中都会是唯一的

------------------------------------------------------ 异常 --------------------------------------------------------------------

public class ExTest {
    public static void t(int i){
        try {  System.out.println("try");   if (i==1){ System.out.println("exception"); throw new Exception( "xxxxxxxx" ); } } 
        catch (Exception e) {System.out.println("catch"); }
        finally { System.out.println("finally");}
    }
    public static void main(String[] args) {
        ExTest.t( 10 );
        System.out.println("---------------------");
        ExTest.t( 1 );
        /* 打印结果是finally都会在try{}catch{}后执行
        try
        finally
        ---------------------
        try
        exception
        catch
        finally
        */
    }
}

PROPAGATION_REQUIRED--支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。  
PROPAGATION_SUPPORTS--支持当前事务,如果当前没有事务,就以非事务方式执行。 
PROPAGATION_MANDATORY--支持当前事务,如果当前没有事务,就抛出异常。 
PROPAGATION_REQUIRES_NEW--新建事务,如果当前存在事务,把当前事务挂起。 
PROPAGATION_NOT_SUPPORTED--以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。 
PROPAGATION_NEVER--以非事务方式执行,如果当前存在事务,则抛出异常。 

 

你可能感兴趣的:(java核心)