首页 文章 精选 留言 我的

精选列表

搜索[学习],共10000篇文章
优秀的个人博客,低调大师

docker学习系列8 实现持续集成

上一节我们构建了一个Node镜像。 这里我有一台已经安装了docker的Linux主机,比如 IP 是154.8.100.124。 我只需在主机上执行 docker run -d -p 3000:3000 finleyma/express,就会部署并运行项目 然后浏览器中输入154.8.100.100:3000 就可以访问了。 这里牵涉到两个问题: 每次修改了项目代码需要重新构建新的镜像,然后push到仓库。 每次还得登录服务器从仓库拉最新的镜像重新运行 ,这样才能反映出变化。 持续集成就能帮我们做到自动化。 不过有几个条件: 需要一个提供持续集成服务的平台,这里我使用国内的 daocloud 他可以接入我们的主机,提供在线 web 的方式管理运行在主机上面的容器。 需要一台能访问的且安装了docker的主机(最好是Linux)。 代码要部署到 github,coding 等 daocloud 支持的代码托管商。 下面介绍下流程: 去daocloud绑定仓库,和主机 根据仓库构建镜像 配置 去 daocloud 申请账号,过程略。 去 管理面板 添加我们的主机 image.png 查看主机上运行的 docker相关的信息 在主机上我们得敲 docker info, docker inspect 等。这里直观的多。 image.png 添加应用 image.png 添加项目,需要绑定 github 或 coding代码源 代码源就是上节的express项目,仓库地址是:https://coding.net/u/finley/p/docker-express-demo。 里面有Dockerfile。我们让daocloud帮忙构建,并且发布到主机上面。 image.png 在流程定义中构建阶段后面添加发布阶段,选择发布到自有主机 image.png 这个过程就是push代码之后,daocloud要根据Dockerfile去构建镜像,构建之前可以要运行测试脚本,构建成功之后可能要发送提醒。 默认是图形化的配置,也可以切换到yaml形式,编写流程定义 daocloud.yml 文件。 这里有个细节,如果你切换到了yaml格式,内容为空。push代码不会触发自动构建的。 稍微吐槽下daocloud做的不够人性化,流程定义没有帮助提示。比如匹配分支,满足条件那块,我不知道能填哪些,你弄个问号,鼠标指上去显示几行描述也好啊 image.png 这样就实现了,每当本地push代码到仓库,daocloud会重新构建镜像然后发布到主机。 image.png 有很多细节没有讲到,请自行查看提供的文档。 参考:http://guide.daocloud.io/dcs/ci-9863687.htmlhttps://github.com/Ye-Ting/docker-ci/blob/master/daocloud.md

优秀的个人博客,低调大师

docker学习系列7 容器化Node项目

本节我们自己打包一个docker镜像并发布到官方的镜像仓库中。这样任何人只需执行以下命令:docker run -d -p 3000:3000 finleyma/express 就可以访问一个简单的express程序了。 需要你有简单的express使用经验 实现过程非常简单: 本地全局安装 npm install express-generator -g 初始化一个express项目 express myapp cd myapp,然后 npm run start,项目就在本地运行了。 我们在项目内建立Dockerfile,内容如下: FROM node:10.8.0-alpine MAINTAINER www.mafeifan.com # 设置工作目录,下面的RUN命令会在工作目录执行 WORKDIR /app # 先拷贝本地的 package.json 和 package-lock 到容器内 # 这样是利用docker的镜像分层机制 COPY package*.json ./ # 安装项目依赖包 # 生产环境可以运行 RUN npm install --only=production 只按照 package.json 中dependencies定义的模块 RUN npm install # 将根目录下的文件都copy到container(运行此镜像的容器)文件系统的app文件夹下 ADD . /app/ # 暴露容器内的3000端口 EXPOSE 3000 # 容器启动时执行的命令,类似npm run start CMD ["npm", "start"] 构建镜像 docker build -t finleyma/express . 别忘了最后的点,表示当前目录 启动容器 docker run -d -p 3000:3000 finleyma/express 可选,登录docker hub, 并提交镜像。docker login, docker push finleyma/express 进入容器 docker run -it --rm finleyma/express:1.0 ash 简要说下参数: -it:这是两个参数,一个是 -i:交互式操作,一个是 -t 终端。我们这里打算进入 容器 执行一些命令并查看返回结果,因此我们需要交互式终端。 --rm:这个参数是说容器退出后随之将其删除。默认情况下,为了排障需求,退出的容器并不会立即删除,除非手动 docker rm。我们这里只是随便执行个命令,看看结果,不需要排障和保留结果,因此使用 --rm 可以避免浪费空间。 ash:因为我们的Node的基础镜像是10.8.0-alpine, alpine的交互式 Shell是ash不是bash,使用bash会提示not found。注意这个细节。 会发现整个项目文件都在容器内。 image.png 简单总结使用Docker的好处: 使用版本方便,比如服务器上跑着node6,而你的项目需要node8以上。使用docker因为是隔离的环境 部署分享也方便,一行命令完事 问题:容器内的 node_modules 是本来就有还是容器执行 npm install 产生的呢? 答案:是构建的时候打包进镜像内了。我们看一下体积,有20M而且进到容器内, ls -l node_modules 时间也是打包的什么,并不是当前时间。 image.png 参考: https://nodejs.org/en/docs/guides/nodejs-docker-webapp/https://www.imooc.com/article/19840https://github.com/nodejs/docker-node/blob/master/docs/BestPractices.md

优秀的个人博客,低调大师

(二)Java并发学习笔记--安全发布对象

逸出的方式 上边关于逸出的概念讲述的很是模糊,下面列举几个逸出的示例。 通过静态变量引用逸出 public static Set<Secret> knownSecrets; public void initialize() { knowsSecrets = new HashSet<Secret>(); } 上边代码示例中,调用initialize方法,发布了knowSecrets对象。当你向knowSecrets中添加一个Secret时,会同时将Secret对象发布出去,原因是可以通过遍历knowSecrets获取到Secret对象的引用,然后进行修改。 通过非静态(私有)方法 class UnsafeStates { private String[] states = new String[]{"AK", "AL"}; public String[] getStates() { return states; } } 以这种方式发布的states会出问题,任何一个调用者都能修改它的内容。数组states已经逸出了它所属的范围,这个本应该私有的数据,事实上已经变成共有的了。 this逸出 public class ThisEscape { public ThisEscape(EventSource source) { source.registerListener(new EventListener() { public void onEvent(Event e) { doSomething(e); } }); } } 在上边代码中,当我们实例化ThisEscape对象时,会调用source的registerListener方法时,便启动了一个线程,而且这个线程持有了ThisEscape对象(调用了对象的doSomething方法),但此时ThisEscape对象却没有实例化完成(还没有返回一个引用),所以我们说,此时造成了一个this引用逸出,即还没有完成的实例化ThisEscape对象的动作,却已经暴露了对象的引用,使其他线程可以访问还没有构造好的对象,可能会造成意料不到的问题。 通过上述示例,个人理解,对逸出的概念应该定义为: 一个对象,超出了它原本的作用域,而可以被其它对象进行修改,而这种修改及修改的结果是无法预测的。换句话说:一个对象发布后,它的状态应该是稳定的,修改是可被检测到的。如果在其它线程修改(或做其它操作)一个对象后导致对象的状态未知,就可以说这个对象逸出了。 总之,一个对象逸出后,不论其它线程或对象是否使用这个逸出的对象都不重要,重要的是,被误用及被误用后的未知结果的风险总是存在的。 PS 书中给出了避免this逸出的方法: public class SafeListener { private final EventListener listener; private SafeListener() { listener = new EventListener() { public void onEvent(Event e) { doSomething(e); } }; } public static SafeListener newInstance(EventSource source) { SafeListener safe = new SafeListener(); source.registerListener(safe.listener); return safe; } } 在这个构造中,我们看到的最大的一个区别就是:当构造好了SafeListener对象之后,我们才启动了监听线程,也就确保了SafeListener对象是构造完成之后在使用的SafeListener对象。 对于这样的技术,书里面也有这样的注释: 具体来说,只有当构造函数返回时,this引用才应该从线程中逸出。构造函数可以将this引用保存到某个地方,只要其他线程不会在构造函数完成之前使用它。 安全发布对象 在静态初始化函数中初始化一个对象引用 将对象的应用保存到volatile类型的域或者AtomicReferance对象中 将对象的引用保存到某个正确构造对象的final类型域中 将对象的引用保存到一个由锁保护的域中。 /** * 懒汉模式(线程不安全) * 单例实例在第一次使用时进行创建 */ @NotThreadSafe public class SingletonExample1 { // 私有构造函数 private SingletonExample1() { } // 单例对象 private static SingletonExample1 instance = null; // 静态的工厂方法 public static SingletonExample1 getInstance() { // 这里同时有两个线程进入就可能同时初始化两个对象 if (instance == null) { instance = new SingletonExample1(); } return instance; } } 懒汉模式本身是线程不安全的,如果想要实现线程安全可以通过synchronized关键字实现: /** * 懒汉模式 * 单例实例在第一次使用时进行创建 */ @ThreadSafe @NotRecommend public class SingletonExample3 { // 私有构造函数 private SingletonExample3() { } // 单例对象 private static SingletonExample3 instance = null; // 静态的工厂方法 public static synchronized SingletonExample3 getInstance() { if (instance == null) { instance = new SingletonExample3(); } return instance; } } 但此中方式不推荐使用,应该它通过同一时间内只允许一个线程来访问的方式实现线程安全,但是却带来了性能上面的开销。 我们可以通过以下方式来实现线程安全: 懒汉模式 -》 volatile + 双重同步锁单例模式 /** * 懒汉模式 -》 双重同步锁单例模式 * 单例实例在第一次使用时进行创建 */ @ThreadSafe public class SingletonExample4 { // 私有构造函数 private SingletonExample4() { } // 1、memory = allocate() 分配对象的内存空间 // 2、ctorInstance() 初始化对象 // 3、instance = memory 设置instance指向刚分配的内存 // JVM和cpu优化,发生了指令重排(多线程 ) // 1、memory = allocate() 分配对象的内存空间 // 3、instance = memory 设置instance指向刚分配的内存 // 2、ctorInstance() 初始化对象 // 单例对象 volatile + 双重检测机制 -> 禁止指令重排 private volatile static SingletonExample4 instance = null; public static SingletonExample4 getInstance() { if (instance == null) { // 双重检测机制 // B synchronized (SingletonExample4.class) { // 同步锁 if (instance == null) { instance = new SingletonExample4(); // A - 3 } } } return instance; } } /** * 饿汉模式 * 单例实例在类装载时进行创建 */ @ThreadSafe public class SingletonExample2 { // 私有构造函数 private SingletonExample2() { } // 单例对象 private static SingletonExample2 instance = new SingletonExample2(); // 静态的工厂方法 public static SingletonExample2 getInstance() { return instance; } } 饿汉模式不会有线程问题,但是在类加载时实例化对象。使用时要考虑两点: 私有构造函数在使用时没有过多的逻辑处理(销毁性能,慢) 这个对象一定会被使用(浪费资源) 在静态代码块中实例化一个对象: /** * 饿汉模式 * 单例实例在类装载时进行创建 */ @ThreadSafe public class SingletonExample6 { // 私有构造函数 private SingletonExample6() { } // 单例对象 private static SingletonExample6 instance = null; static { instance = new SingletonExample6(); } // 静态的工厂方法 public static SingletonExample6 getInstance() { return instance; } public static void main(String[] args) { System.out.println(getInstance().hashCode()); System.out.println(getInstance().hashCode()); } } 枚举模式: /** * 枚举模式:最安全 */ @ThreadSafe @Recommend public class SingletonExample7 { // 私有构造函数 private SingletonExample7() { } public static SingletonExample7 getInstance() { return Singleton.INSTANCE.getInstance(); } private enum Singleton { INSTANCE; private SingletonExample7 singleton; // JVM保证这个方法绝对只调用一次 Singleton() { singleton = new SingletonExample7(); } public SingletonExample7 getInstance() { return singleton; } } }

优秀的个人博客,低调大师

设计模式快速学习(三)单例模式

单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。 这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。 优点 1、在内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例(比如管理学院首页页面缓存)。 2、避免对资源的多重占用(比如写文件操作)。 缺点 没有接口,不能继承,与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么样来实例化。 实现方式一:懒汉式(线程不安全) 懒汉式:就是用的时候再进行实例化对象。 public class Singleton { private static Singleton instance; private Singleton (){} public static Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } } 这种实现方式不支持多线程,因为没有同步锁,多线程下不能正常工作。 实现方式二:懒汉式(线程安全) public class Singleton { private static Singleton instance; public static synchronized Singleton getInstance(){ if (instance == null){ instance = new Singleton(); } return instance; } } 可以在多线程环境下使用,但是效率太低。 优点:一个对象初始化一次,节省内存。缺点:必须用synchronized来维持单例,没效率。 实现方式三:饿汉式(线程安全) public class Singleton { private static Singleton instance = new Singleton(); public static Singleton getInstance(){ return instance; } } 因为它作为静态资源,所以在类装载时就被实例化 优点:没有加锁,执行效率会提高。缺点:类加载时就初始化,浪费内存。 实现方式四:双检锁/双重校验锁DCL(线程安全) public class Singleton { private static Singleton instance; public static Singleton getInstance(){ if (instance == null){ synchronized (Singleton.class){ if (instance == null){ instance = new Singleton(); } } } return instance; } } 采用双锁机制,安全且在多线程情况下能保持高性能。详细了解请点击:Java并发编程 -- 单例模式线程安全问题 实现方式五:登记式/静态内部类(线程安全) public class Singleton { private static Singleton instance; private static class SingletonHandler{ private static final Singleton INSTANCE = new Singleton(); } public static Singleton getInstance(){ return SingletonHandler.INSTANCE; } } 这种方式可以说是恶汉式的变通版,SingletonHandler没有被主动使用的情况下是不会实例化Singleton对象的,所以这样做,既能达到lazy式的加载,又能保证线程安全。 实现方式六:枚举类(线程安全) public enum Singleton { INSTANCE; public void myMethod() { System.out.println("enum instance test"); } } 它不仅能避免多线程同步问题,而且还自动支持序列化机制,防止反序列化重新创建新的对象,绝对防止多次实例化。 测试: public class Main { public static void main(String[] args) { Singleton singleton = Singleton.INSTANCE; singleton.myMethod(); } } enum instance test 总结 不建议使用第 1 种和第 2 种懒汉方式,建议使用第 3 种饿汉方式。只有在要明确实现 lazy loading 效果时,才会使用第 5 种登记方式。如果涉及到反序列化创建对象时,可以尝试使用第 6 种枚举方式。

优秀的个人博客,低调大师

Java 学习(22)---(类的加载与反射)

类的加载 当程序要使用某个类时, 如果该类还未被加载到内存中, 则系统会通过加载,连接,初始化三步来实现对这个类进行初始化。 加载 就是指将 class文件读入内存,并为之创建一个 Class对象。任何类被使用时系统都会建立一个 Class对象。 连接 验证是否有正确的内部结构,并和其他类协调一致准备负责为类的静态成员分配内存,并设置默认初始化值解析将类的二进制数据中的符号引用替换为直接引用 初始化 初始化步骤: 2、类初始化时机 1.创建类的实例 2.访问类的静态变量,或者为静态变量赋值调用类的静态方法 3.使用反射方式来强制创建某个类或接口对应的 java.lang.Class对象 4.初始化某个类的子类 5.直接使用 java.exe命令来运行某个主类 3、类加载器 类加载器: 负责将.class文件加载到内在中,并为之生成对应的 Class对象。 虽然我们不需要关心类加载机制, 但是了解这个机制我们就能更好的理解程序的运行。 类加载器的组成 1. BootstrapClassLoader根类加载器 也被称为引导类加载器,负责 Java核心类的加载 比如 System,String 等。在 JDK中 JRE的 lib 目录下 rt.jar 文件中 2. ExtensionClassLoader扩展类加载器 负责JRE的扩展目录中 jar包的加载。 在 JDK中 JRE的 lib 目录下 ext 目录 3.SysetmClassLoader系统类加载器 负责在JVM启动时加载来自 java命令的class文件,以及classpath环境变量所指定的 jar包和类路径 反射 JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为 java语言的反射机制。 要想解剖一个类 ,必须先要获取到该类的字节码文件对象。而解剖使用的就是 Class 类中的方法.所以先要获取到每一个字节码文件对应的 Class类型的对象. 1.获取 class文件对象的方式: Object类的getClass() 方法 如: s.getClass(); 数据类型的静态属性 class 如 : Student.class; Class类中的静态方法:publicstaticClassforName(String className) ; //完整类名 如:Class.forName( “cn.itcast.Student ”); 一般我们到底使用谁呢 ? A:自己玩 任选一种, 第二种比较方便 B:开发 第三种 因为第三种是一个字符串,而不是一个具体的类名。这样我们就可以把这样的字符串配置到配置文件中 2.获取构造方法 A、得到构造方法对象 publicConstructor[] getConstructors() : 所有公共构造方法 publicConstructor[] getDeclaredConstructors(): 所有构造方法 获取单个构造方法 public Constructor<T> getConstructor(Class<?>...parameterTypes) 参数表示:你要获取的构造方法的构造参数个数及数据类型的 class字节码文件对象 B、创建对象 public T newInstance(Object...initargs) 使用此 Constructor 对象表示的构造方法来创建该构造方法的声明类的新实例, 并用指定的初始化参数初始化该实例 // 通过无参构造器 Constructor con = c.getConstructor(); Object obj = con.newInstance(); // 通过带参构造 Constructor con = c.getConstructor(String. class , int . class ,String. class ); Object obj = con.newInstance( " 林青霞 " , 27, " 北京 " ); // 通过私用构造方法 Constructor con = c.getDeclaredConstructor(String.class); // 暴力访问,值为 true 则指示反射的对象在使用时应该取消 Java 语言访问检查。 con.setAccessible( true ); Object obj = con.newInstance(" 风清扬 "); 3.获取成员变量 public FieldgetField(Stringname) ; 返回一个 Field 对象,它反映此 Class对象所表示的类或接口的指定公共成员字段 publicField[]getFields() ; 返回一个包含某些 Field 对象的数组,这些对象反映此 Class对象所表示的类或接口的所有可访问公共字段。 // 获取字节码文件对象 Class c = Class.forName("cn.itcast_01.Person"); 通过无参构造方法创建对象Constructor con = c.getConstructor(); Object obj = con.newInstance(); // 获取单个的成员变量 Field addressField = c.getField("address" ); // public void set(Object obj,Object value) // 将指定对象变量上此 Field 对象表示的字段设置为指定的新值。 addressField.set(obj, " 北京 " ); // 给obj 对象的 addressField 字段设置值为 " 北京 " // 获取 name并对其赋值 // NoSuchFieldException ,name属性为私有 Field nameField = c.getDeclaredField("name" ); // IllegalAccessException nameField.setAccessible(true); nameField.set(obj, " 林青霞 " ); // 获取 age 并对其赋值 Field ageField = c.getDeclaredField("age"); ageField.setAccessible( true); ageField.set(obj, 27); 4.获取成员方法并执行 public Method getMethod(Stringname,Class<?>...parameterTypes) ; 第一个参数表示的方法名,第二个参数表示的是方法的参数的 class 类型 publicMethod[]getMethods() ;获取自己的包括父亲的公共方法 public Method getDeclaredMethod(Stringname, Class<?>... parameterTypes) 获取自己的包括父亲的任意方法 publicMethod[]getDeclaredMethods() ; 获取自己的包括父亲的所有方法 执行 Method类的方法:publicObjectinvoke(Objectobj,Object...args) ; 返回值是Object 接收,第一个参数表示对象是谁, 第二参数表示调用该方法的实际 参数 执行私有方法: publicvoidsetAccessible(boolean flag) ;取消访问检查 案例 通过配置文件运行类中的方法 // 加载键值对数据 Properties prop = new Properties(); FileReader fr =new FileReader( "class.txt"); prop.load(fr); fr.close(); // 获取数据 String className = prop.getProperty("className" ); String methodName = prop.getProperty("methodName"); //反射 Class c = Class.forName(className); Constructor con = c.getConstructor(); Object obj = con.newInstance(); // 调用方法 Method m = c.getMethod(methodName); m.invoke(obj); 在 ArrayList<Integer> 这个集合里添加一个字符串数据 通过反射来实现 ArrayList<Integer> array = new ArrayList<Integer>(); // array.add("hello"); // array.add(10); Class c = array.getClass(); //集合 ArrayList 的class 文件对象 Method m = c.getMethod( "add" , Object. class ); m.invoke(array, "hello" ); // 调用 array 的add 方法,传入的值是 hello m.invoke(array, "world" ); m.invoke(array, "java" ); 5.代理模式 1)动态代理 1)代理对象,不需要实现接口,目标对象必须实现接口; 2)代理对象的生成,是利用 JDKAP,I 动态的在内存中构建代理对象 (需要我们指定创建代理对象/目标对象实现的接口的类型; ); 3) 动态代理, JDK代理,接口代理; publicclass ProxyFactory { // 维护一个目标对象 private Object target ; public ProxyFactory(Object target){ this. target= target; } // 给目标对象,生成代理对象 public Object getProxyInstance() { return Proxy.newProxyInstance( target .getClass().getClassLoader(), target .getClass().getInterfaces(), newInvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System. out .println( " 开启事务 " ); // 执行目标对象方法 Object returnValue = method.invoke( target , args); System. out .println( " 提交事务 " ); return returnValue; } }); } } // 目标对象 IUserDao target = new UserDao(); // [原始的类型 class cn.itcast.b_dynamic.UserDao] System.out.println(target.getClass()); // 给目标对象,创建代理对象 IUserDao proxy=(IUserDao) newProxyFactory(target).getProxyInstance(); // class $Proxy0 内存中动态生成的代理对象 System.out.println(proxy.getClass()); // 执行方法[代理对象] proxy.save(); 2) 静态代理 1)代理对象,要实现与目标对象一样的接口; 2)举例 : 保存用户 (模拟) Dao , 直接保存 DaoProxy, 给保存方法添加事务处理 publicclass UserDaoProxy implements IUserDao{ // 接收保存目标对象 private IUserDao target; public UserDaoProxy(IUserDao target) { this. target= target; } @Override publicvoid save() { System. out .println( " 开始事务 ..." ); target .save(); // 执行目标对象的方法 System. out .println(" 提交事务 ..." ); } } 总结静态代理: 1)可以做到在不修改目标对象的功能前提下,对目标对象功能扩展。 2)缺点:因为代理对象,需要与目标对象实现一样的接口。所以会有很多代理类,类太多。 一旦接口增加方法,目标对象与代理对象都要维护。

优秀的个人博客,低调大师

Java 学习(23)---(IO流之字节流)

字节流 IO 用于在设备间进行数据传输的操作 1.分类: 字节流 |-- 字节输入流 InputStream int read(): 一次读取一个字节 int read(byte[] bys): 一次读取一个字节数组 |--FileInputStream |--BufferedInputStream |-- 字节输出流 OutputStream void write(int by): 一次写一个字节 void write(byte[] bys,intindex,intlen): 一次写一个字节数组的一部分 |--FileOutputStream |--BufferedOutputStream 字符流 |-- 字符输入流 Reader int read(): 一次读取一个字符 int read(char[] chs): 一次读取一个字符数组 |--InputStreamReader |--FileReader |--BufferedReader String readLine(): 一次读取一个字符串 |-- 字符输出流 Writer void write(int ch):一次写一个字符 void write(char[] chs,intindex,intlen): 一次写一个字符数组的一部分 |--OutputStreamWriter |--FileWriter |--BufferedWriter void newLine(): 写一个换行符 void write(String line): 一次写一个字符串 注意: a:如果我们没有明确说明按照什么分,默认按照数据类型分。 b:除非文件用 windows 自带的记事本打开我们能够读懂, 才采用字符流,否则建议使用字节流。 2.FileOutputStream 写出数据 A:操作步骤 a:创建字节输出流对象 b: 调用 write() 方法 c:释放资源 B:API 构造方法 FileOutputStream(File file) FileOutputStream(File file ,booleanappend) FileOutputStream(Stringname) FileOutputStream(Stringname ,booleanappend); 如果第二个参数为 true,则将字节写入文件末尾处,而不是写入文件开始处。 成员方法 public voidwrite(intb): 写一个字节 public voidwrite(byte[] b): 写一个字节数组 public void write(byte[] b,intoff,intlen) :写一个字节数组的一部分 publicvoidclose(); 关闭此文件输出流并释放与此流有关的所有系统资源。 C:代码体现: // 创建字节输出流对象 // FileOutputStream(File file) // File file = new File("fos.txt"); // FileOutputStream fos = new FileOutputStream(file); // FileOutputStream(String name) FileOutputStream fos = newFileOutputStream("fos.txt"); /* *创建字节输出流对象了做了几件事情: *A: 调用系统功能去创建文件 *B: 创建 fos 对象 *C: 把 fos 对象指向这个文件 */ // 写数据 fos.write( fos.write("hello,IO".getBytes()); fos.write( fos.write(""java"".getBytes()); // 释放资源 // 关闭此文件输出流并释放与此流有关的所有系统资源。 fos.close(); /* *为什么一定要 close() 呢 ? *A: 让流对象变成垃圾,这样就可以被垃圾回收器回收了 *B: 通知系统去释放跟该文件相关的资源 */ //java.io.IOException: Stream Closed //fos.write("java".getBytes()); 加入异常处理的代码: FileOutputStream fos = null ; try { // fos = new FileOutputStream("z:\\fos4.txt"); fos = newFileOutputStream("fos4.txt""fos4.txt"); fos .write("java" .getBytes()); } catch (FileNotFoundException e ) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { // 如果 fos 不是 null ,才需要 close() if ( fos != null ) { // 为了保证 close() 一定会执行,就放到这里了 try { fos .close(); } catch (IOException e) { e.printStackTrace(); } } } C:要注意的问题 a:创建字节输出流对象做了几件事情 ? 1.调用系统功能去创建文件 2. 创 建 fos对 象C:把fos对象指向这个文件 b:为什么要 close()? 1. 让流对象变成垃圾,这样就可以被垃圾回收器回收了 2.通知系统去释放跟该文件相关的资源 c:如何实现数据的换行 ? 写入换行符号即可 fos.write("\r\n".getBytes()); d:如何实现数据的追加写入 ? new FileOutputStream("fos3.txt", true); 3.FileInputStream 读取数据 A:操作步骤 1.创建字节输入流对象 2.调用 read() 方法 3.释放资源 B: API 构造方法 FileOutputStream(File file) FileOutputStream(String name) 成员方法 int read(): 一次读取一个字节 int read(byte[] b): 一次读取一个字节数组 代码:复制 FileInputStream fis = newFileInputStream( "fos.txt" ); // 方式 1 int by = 0; while ((by=fis.read())!=-1) { System. out .print(( char ) by ); } // 方式 2 byte [] bys = newbyte [1024]; int len = 0; while ((len=fis.read(bys))!=-1) { System. out .print( new String( bys ,0, len )); } fis.close(); 案例: // 高效字节流一次读写一个字节数组: publicstaticvoid method4(String srcString, String destString) throws IOException { BufferedInputStream bis = newBufferedInputStream( new FileInputStream (srcString)); newBufferedOutputStream(new FileOutputStream(destString)); byte [] bys = newbyte [1024]; int len = 0; while ((len = bis.read(bys)) != -1) { bos.write(bys, 0, len); } bos.close(); bis.close(); } // 高效字节流一次读写一个字节: publicstaticvoid method3(String srcString, String destString) throws IOException { BufferedInputStream bis = newBufferedInputStream( new FileInputStream( srcString)); BufferedOutputStream bos = newBufferedOutputStream( new FileOutputStream(destString)); int by = 0; while ((by = bis.read()) != -1) { bos.write(by); } bos.close(); bis.close(); } // 基本字节流一次读写一个字节数组 publicstaticvoid method2(String srcString, String destString) throws IOException { FileInputStream fis = new FileInputStream(srcString); FileOutputStream fos = new FileOutputStream(destString); byte [] bys = newbyte [1024]; int len = 0; while ((len = fis.read(bys)) != -1) { fos.write(bys, 0, len); } fos.close(); fis.close(); } // 基本字节流一次读写一个字节 publicstaticvoid method1(String srcString, String destString) throws IOException { FileInputStream fis = new FileInputStream(srcString); FileOutputStream fos = new FileOutputStream(destString); int by = 0; while ((by = fis.read()) != -1) { fos.write(by); } fos.close(); fis.close(); } 4.字节缓冲区流 通过定义数组的方式确实比以前一次读取一个字节的方式快很多, 所以,看来有一个缓冲区还是非常好的。 既然是这样的话,那么, java开始在设计的时候,它也考虑到了这个问题,就专门提供了带缓冲区的字节类。 这种类被称为:缓冲区类 (高效类) A:BufferedOutputStream B:BufferedInputStream

优秀的个人博客,低调大师

Java 学习(24)---(IO流之字符流)

字符流 字节流操作中文数据不是特别的方便,所以就出现了转换流。转换流的作用就是把字节流转换字符流来使用。 转换流其实是一个字符流 字符流 =字节流 +编码表 编码表 A:就是由字符和对应的数值组成的一张表 B:常见的编码表 ASCII ISO-8859-1 GB2312 GBK GB18030 UTF-8 C:字符串中的编码问题编码 String -- byte[] 解码 byte[] -- String IO 流中的编码问题 A:OutputStreamWriter OutputStreamWriter(OutputStreamos): 默 认 编 码 , GBK OutputStreamWriter(OutputStreamos,StringcharsetName): 指定编码。 B:InputStreamReader InputStreamReader(InputStream is): 默 认 编 码 , GBK InputStreamReader(InputStreamis,StringcharsetName): 指定编码 C:编码问题其实很简单编码只要一致即可 字符流 Reader |--InputStreamReader |--FileReader |--BufferedReader Writer |--OutputStreamWriter |--FileWriter |--BufferedWriter 案例:复制文本文件 (5种方式 ) // 基本字符流一次读写一个字符 privatestaticvoid method1(String srcString, String destString) throws IOException { FileReader fr = newFileReader(srcString); FileWriter fw = newFileWriter(destString); int ch = 0; while ((ch = fr.read()) != -1) { fw.write(ch); } fw.close(); fr.close(); } // 基本字符流一次读写一个字符数组 privatestaticvoid method2(String srcString, String destString) throws IOException { FileReader fr = newFileReader(srcString); FileWriter fw = newFileWriter(destString); char [] chs = newchar [1024]; int len = 0; while ((len = fr.read(chs)) != -1) { fw.write(chs, 0, len); } fw.close(); fr.close(); } // 字符缓冲流一次读写一个字符 privatestaticvoid method3(String srcString, String destString) throws IOException { BufferedReader br = newBufferedReader(newFileReader(srcString)); BufferedWriter bw = newBufferedWriter(newFileWriter(destString)); int ch = 0; while ((ch = br.read()) != -1) { bw.write(ch); } bw.close(); br.close(); } // 字符缓冲流一次读写一个字符数组 privatestaticvoid method4(String srcString, String destString) throws IOException { BufferedReader br = newFileReader(srcString)); newBufferedReader(newFileWriter(destString)); char [] chs = newchar [1024]; int len = 0; while ((len = br.read(chs)) != -1) { bw.write(chs, 0, len); } bw.close(); br.close(); } // 字符缓冲流一次读写一个字符串 privatestaticvoid method5(String srcString, String destString) throws IOException { BufferedReader br = newFileReader(srcString)); newBufferedReader(newFileWriter(destString)); String line = null; while ((line = br.readLine()) != null) { bw.write(line); bw.newLine(); bw.flush(); }

优秀的个人博客,低调大师

分布式系统学习共性总结:

1.归纳法: 对标与参考 适配与裁剪 2..术法道,深入源码体系 先分析该系统是数据存储还是计算系统。 如果是数据存储系统,从数据分布和副本策略开始入手 哈希:ElasticSearch 范围:HBase 数据量:HDFS 一致性:Cassan 副本读取 副本更新 副本切换 如果是数据处理问题,从数据投递策略入手。 离线 实时 微批 at most once at least once exactly once 2 .读对应系统架构图,对应着常用的架构模型,每个组件和已有的系统进行类比,想一下这个组件类似于hdfs的namenode等等,最后在脑海里梳理下数据流的整个流程。 3.在了解了系统的大概,着重看下文档中fault tolerence章节,看系统如何容错,或者自己可以预先问些问题,比如如果一个节点挂了、一个任务挂了系统是如何处理这些异常的,带着问题看文档。 4.文档详细读了一遍,就可以按照官方文档写些hello world的例子了,详细查看下系统配置项,随着工作的深入就可以看些系统的细节和关键源码了。 转自:https://blog.csdn.net/tg229dvt5i93mxaq5a6u/article/details/78266424

优秀的个人博客,低调大师

自定义(带属性)标签学习笔记

前言: 前面我学了核心的标签但是如果我想要实现的内容核心标签 和jstl标签库不能解决怎么办?有人说可以用jsp脚本,但是jsp主要是展示数据用的,不应该大量出现java代码才对。这个时候自定义标签就出现了。(也有人说jsp耦合度太高了,这些姑且不考虑)自定义标签目的: 替代jsp脚本,用于在jsp页面中执行java代码 第一步:编写一个普通的java类,继承SimpleTagSupport类,叫标签处理器类 注意:SimpleTagSupport需要导包jsp-api.jar,这个可以在Tomcat的lib中找到 重点:自定义标签的内容要覆盖doTag方法 自定义标签的执行过程 问题: http://localhost:8080/day14/01.hellotag.jsp 如何访问到自定义标签? 前提: tomcat服务器启动时,加载到每个web应用,加载每个web应用的WEB-INF目录下的所有文件!!!例如。web.xml, tld文件!!! 1)访问01.hellotag.jsp资源 2)tomcat服务器把jsp文件翻译成java源文件->编译class->构造类对象->调用_jspService()方法 3)检查jsp文件的taglib指令,是否存在一个名为http://gz.itcast.cn的tld文件。如果没有,则报错 4)上一步已经读到itcast.tld文件 5)读到<itcast:showIp> 到itcast.tld文件中查询是否存在<name>为showIp的<tag>标签 6)找到对应的<tag>标签,则读到<tag-class>内容 7)得到 gz.itcast.a_tag.ShowIpTag 构造ShowIpTag对象,然后调用ShowIpTag里面的方法 自定义标签处理器类的生命周期 SimpleTag接口: void setJspContext(JspContext pc) --设置pageContext对象,传入pageContext(一定调用) 通过getJspCotext()方法得到pageContext对象 void setParent(JspTag parent) --设置父标签对象,传入父标签对象,如果没有父标签,则不 调用此方法。通过getParent()方法得到父标签对象。 void setXXX(值) --设置属性值。 void setJspBody(JspFragment jspBody) --设置标签体内容。标签体内容封装到JspFragment对象 中,然后传入JspFragment对象。通过getJspBody()方法 得到标签体内容。如果没有标签体内容,则不会调 用此方法 void doTag() --执行标签时调用的方法。(一定调用) 我编写了两个:直接贴代码好了:代码1实现了 像浏览器输出浏览器的IP地址: package show; import java.io.IOException; import javax.servlet.http.HttpServletRequest; import javax.servlet.jsp.JspException; import javax.servlet.jsp.JspWriter; import javax.servlet.jsp.PageContext; import javax.servlet.jsp.tagext.SimpleTagSupport; public class ShowIp extends SimpleTagSupport { @Override public void doTag() throws JspException, IOException { PageContext pageContext=(PageContext)this.getJspContext(); HttpServletRequest httpServletRequest=(HttpServletRequest)pageContext.getRequest(); String string=httpServletRequest.getRemoteHost(); JspWriter jspWriter=pageContext.getOut(); jspWriter.write(string); } } 代码2:比较杂,主要有带属性的标签还有对标签内容进行操作:1)控制标签体内容是否输出2)控制标签余下内容是否输出3)控制重复输出标签体内容4)改变标签体内容5)带属性的标签 public class other extends SimpleTagSupport { private Integer num; public void setNum(Integer num) { this.num = num; } @Override public void doTag() throws JspException, IOException { //输出属性 System.out.println(num); /* 1)控制标签内容是否输出 输出: 调用jspFrament.invoke(); 不输出: 不调用jspFrament.invoke(); 往浏览器输出内容,invoke为null就是默认往浏览器输出 */ this.getJspBody().invoke(null); JspFragment jspFragment= this.getJspBody(); String context=jspFragment.toString(); //控制重复输出标签体内容 for(int i=0;i<2;i++) { jspFragment.invoke(null); } //改变标签体内容 StringWriter stringWriter=new StringWriter(); //标签内容存到临时StringWriter中 jspFragment.invoke(stringWriter); String string=stringWriter.toString(); string=string.toUpperCase(); this.getJspContext().getOut().write(string); //控制标签余下内容是否输出 //不输出: 抛出SkipPageException异常 throw new SkipPageException(); } } 第二步:在web项目的WEB-INF目录下建立itcast.tld文件,这个tld叫标签库的声明文件。 <?xml version="1.0" encoding="UTF-8" ?> <taglib xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd" version="2.0"> <description>自定义</description> <display-name>jjc</display-name> <tlib-version>1.1</tlib-version> <short-name>item</short-name> <uri>jiangjiacong</uri> <tag> <name>showIp</name> <tag-class>show.ShowIp</tag-class> <body-content>scriptless</body-content> </tag> <tag> <name>context</name> <tag-class>other.other</tag-class> <!-- empt:表示没有标签体 JSP:表示标签体可以包含JSP代码 scriptless:表示标签体可以包含EL表达式和JSP动作元素,但不能包含JSP的脚本元素 tagdependent:表示标签体交由标签本身去解析处理。即在标签体中所写的任何代码都会原封不动地传给标签处理器 --> <body-content>scriptless</body-content> <!-- 属性声明 --> <attribute> <!-- 属性名称 --> <name>num</name> <!-- 是否必填 --> <required>true</required> <!-- 是否支持EL表达式 --> <rtexprvalue>true</rtexprvalue> </attribute> </tag> </taglib> 注意: <body-content>:是输出标签体内容格式 empt:表示没有标签体 JSP:表示标签体可以包含JSP代码 scriptless:表示标签体可以包含EL表达式和JSP动作元素,但不能包含JSP的脚本元素 tagdependent:表示标签体交由标签本身去解析处理。即在标签体中所写的任何代码都会原封不动地传给标签处理器 一般自定义标签都是scriptless 接下来 jsp页面要引用自定义标签: <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@taglib uri="jiangjiacong" prefix="item"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Insert title here</title> </head> <body> <%Integer age=133; pageContext.setAttribute("agevalue", age); %> <%-- <item:showIp>sdf</item:showIp> --%> <item:context num="${agevalue}">${10>2 }<br/></item:context> </body> </html> 重点: <%@taglib uri="jiangjiacong" prefix="item"%>

资源下载

更多资源
优质分享App

优质分享App

近一个月的开发和优化,本站点的第一个app全新上线。该app采用极致压缩,本体才4.36MB。系统里面做了大量数据访问、缓存优化。方便用户在手机上查看文章。后续会推出HarmonyOS的适配版本。

Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。

用户登录
用户注册