java 性能 优化_Java十大简单性能优化

java 性能 优化

关于“ web scale ”这个流行词有很多炒作,人们花了很多时间来重新组织他们的应用程序体系结构,以使其系统“规模化”。

但是什么扩展,我们如何确保可以扩展?

缩放的不同方面

上面提到的炒作主要是关于扩展负载 ,即确保适用于1个用户的系统也能很好地适用于10个用户,100个用户或数百万个用户。 理想情况下,您的系统应尽可能“无状态”,以便可以在网络中的任何处理单元上转移和转换真正保留的少量状态。 当负载是您的问题时,延迟可能就没有了,因此如果单个请求花费50-100ms没关系。 这通常也称为横向扩展

扩展的一个完全不同的方面是扩展性能 ,即,确保适用于1条信息的算法也适用于10条或100条或数百万条。 Big O Notation最好地描述了这种缩放是否可行。 延迟是扩展性能的杀手。 您想尽一切可能将所有计算保持在一台计算机上。 这通常也称为放大

如果有免费午餐之类的东西( 没有 ),我们可以无限地组合扩大规模和扩大规模。 无论如何,今天,我们将研究一些非常简单的方法来改善性能。

大O符号

Java 7的ForkJoinPool以及Java 8的并行Stream有助于并行化内容,这在将Java程序部署到多核处理器计算机上时非常有用。 与在网络上的不同计算机上进行扩展相比,这种并行性的优势在于您几乎可以完全消除延迟影响,因为所有内核都可以访问同一内存。

但是,不要被并行性的效果所迷惑! 请记住以下两件事:

  • 并行主义吞噬了您的核心。 这对于批处理非常有用,但是对于异步服务器(例如HTTP)来说却是一场噩梦。 在过去的几十年中,我们使用单线程servlet模型是有充分的理由的。 因此,并行性仅在扩大规模时有用。
  • 并行性对算法的Big O表示法没有影响。 如果您的算法是O(n log n) ,并且让该算法在c核上运行,那么您仍将拥有O(n log n / c)算法,因为c在算法复杂度上是微不足道的常数。 您将节省时钟时间,但不会降低复杂性!

当然,提高性能的最佳方法是降低算法复杂度。 当然,杀手是实现O(1)或准O(1) ,例如HashMap查找。 但这并不总是可能的,更不用说轻松了。

如果您无法降低复杂性,只要找到合适的位置,只要对算法真正重要的地方进行调整,您仍然可以获得很多性能。 假定算法的以下直观表示形式:

算法2

如果要处理单个数量级,该算法的总体复杂度为O(N 3 )O(N x O x P) 。 但是,在分析此代码时,您可能会发现一个有趣的场景:

  • 在开发框中,左分支( N -> M -> Heavy operation )是您可以在分析器中看到的唯一分支,因为OP的值在开发样本数据中很小。
  • 但是,在生产中,右分支( N -> O -> P -> Easy operationNOPE )确实会造成麻烦。 您的运营团队可能已经使用AppDynamics或DynaTrace或某些类似的软件解决了这一问题。

没有生产数据,您可能会Swift得出结论并优化“繁重的操作”。 您将产品运送到生产中,并且修复无效。

除了以下事实外,没有最佳的黄金法则:

  • 精心设计的应用程序更容易优化
  • 过早的优化不会解决任何性能问题,但会使您的应用程序设计欠佳,从而使优化变得更加困难

足够的理论。 假设您已找到问题所在的正确分支。 很容易在生产中吹起一个非常简单的操作,因为它被称为很多次(如果NOP大)。 请在不可避免的O(N 3 )算法的叶节点存在问题的情况下阅读本文。 这些优化不会帮助您扩展规模。 他们将帮助您暂时节省客户的时间,将整个算法的困难改进推迟到以后!

以下是Java中最容易进行的10个性能优化:

1.使用StringBuilder

这几乎是所有Java代码中的默认设置。 尽量避免使用+运算符。 当然,您可能会争辩说它仍然只是StringBuilder语法糖,例如:

String x = "a" + args.length + "b";

…编译成

0  new java.lang.StringBuilder [16]3  dup4  ldc <String "a"> [18]6  invokespecial java.lang.StringBuilder(java.lang.String) [20]9  aload_0 [args]
10  arraylength
11  invokevirtual java.lang.StringBuilder.append(int) : java.lang.StringBuilder [23]
14  ldc <String "b"> [27]
16  invokevirtual java.lang.StringBuilder.append(java.lang.String) : java.lang.StringBuilder [29]
19  invokevirtual java.lang.StringBuilder.toString() : java.lang.String [32]
22  astore_1 [x]

但是会发生什么,如果以后需要用可选部分修改String呢?

String x = "a" + args.length + "b";if (args.length == 1)x = x + args[0];

现在,您将拥有第二个StringBuilder ,它不必要地消耗了堆内存,从而给GC带来了压力。 改写这个:

StringBuilder x = new StringBuilder("a");
x.append(args.length);
x.append("b");if (args.length == 1);x.append(args[0]);

带走

在上面的示例中,如果您使用显式StringBuilder实例,或者依赖Java编译器为您创建隐式实例,则可能完全不相关。 但是请记住,我们在NOPE分支中 。 每个CPU周期我们都在浪费像GC这样愚蠢的东西,或者分配StringBuilder的默认容量,我们浪费的时间是N x O x P次。

根据经验,请始终使用StringBuilder而不是+运算符。 并且如果可以的话,如果您的String构建起来比较复杂,则可以跨多个方法保留StringBuilder引用。 这是jOOQ在生成复杂SQL语句时所做的。 只有一个StringBuilder可以“遍历”您的整个SQL AST(抽象语法树)

为了大声喊叫,如果仍然有StringBuffer引用,请用StringBuilder替换它们。 您实际上几乎不需要同步正在创建的字符串。

2.避免使用正则表达式

正则表达式相对便宜且方便。 但是,如果您位于NOPE分支中 ,那么它们将是您最糟糕的事情。 如果您绝对必须在计算密集型代码节中使用正则表达式,则至少要缓存Pattern引用,而不要一直重新编译它:

static final Pattern HEAVY_REGEX = Pattern.compile("(((X)*Y)*Z)*");

但是如果你的正则表达式真的很傻

String[] parts = ipAddress.split("\\.");

…那么您真的最好使用普通的char[]或基于索引的操作。 例如,这个完全不可读的循环执行相同的操作:

int length = ipAddress.length();
int offset = 0;
int part = 0;
for (int i = 0; i < length; i++) {if (i == length - 1 || ipAddress.charAt(i + 1) == '.') {parts[part] = ipAddress.substring(offset, i + 1);part++;offset = i + 2;}
}

……这也说明了为什么您不应该进行任何过早的优化。 与split()版本相比,这是无法维护的。

挑战:读者中的聪明人可能会找到更快的算法。

带走

正则表达式很有用,但要付出一定的代价。 如果您深入了解NOPE分支 ,则必须不惜一切代价避免使用正则表达式。 提防使用正则表达式的各种JDK String方法,例如String.replaceAll()String.split()

请改用诸如Apache Commons Lang之类的流行库来进行String操作。

3.不要使用iterator()

现在,此建议实际上并不适用于一般用例,而仅适用于NOPE分支的深处。 但是,您应该考虑一下。 编写Java-5样式的foreach循环很方便。 您可以完全忘记循环内部,然后编写:

for (String value : strings) {// Do something useful here
}

但是,每次遇到此循环时,如果stringsIterable ,则将创建一个新的Iterator实例。 如果您使用ArrayList ,这将在堆上分配一个3 ints的对象:

private class Itr implements Iterator<E> {int cursor;int lastRet = -1;int expectedModCount = modCount;// ...

相反,您可以编写以下等效循环,并仅在堆栈上“浪费”一个int值,这非常便宜:

int size = strings.size();
for (int i = 0; i < size; i++) {String value : strings.get(i);// Do something useful here
}

…或者,如果您的列表没有真正改变,您甚至可以对其数组版本进行操作:

for (String value : stringArray) {// Do something useful here
}

带走

从可写性和可读性以及从API设计的角度来看,迭代器,Iterable和foreach循环都非常有用。 但是,它们为每次迭代在堆上创建一个小的新实例。 如果您多次运行此迭代,则要确保避免创建此无用的实例,而改为编写基于索引的迭代。

讨论区

关于上述部分的一些有趣的分歧(特别是用索引访问代替Iterator用法) 已经在Reddit上进行了讨论 。

4.不要调用该方法

一些方法简单昂贵。 在我们的NOPE分支示例中,叶子上没有这样的方法,但是您很可能有一个。 让我们假设您的JDBC驱动程序需要经历难以置信的麻烦才能计算ResultSet.wasNull()的值。 您自己SQL框架代码可能如下所示:

if (type == Integer.class) {result = (T) wasNull(rs, Integer.valueOf(rs.getInt(index)));
}// And then...
static final <T> T wasNull(ResultSet rs, T value) 
throws SQLException {return rs.wasNull() ? null : value;
}

每次您从结果集中获取一个int ,此逻辑将立即调用ResultSet.wasNull() 。 但是getInt()合同的内容为:

返回:列值; 如果值为SQL NULL,则返回值为0

因此,对上述内容进行简单但可能极大的改进将是:

static final <T extends Number> T wasNull(ResultSet rs, T value
) 
throws SQLException {return (value == null || (value.intValue() == 0 && rs.wasNull())) ? null : value;
}

因此,这很容易:

带走

不要在算法的“叶子节点”中调用昂贵的方法,而要缓存调用,或者在方法合同允许的情况下避免调用。

5.使用原语和堆栈

上面的示例来自jOOQ ,它使用了许多泛型,因此被迫对byteshortintlong使用包装器类型-至少在泛型将在Java 10和项目Valhalla中实现特殊化之前。 但是您的代码中可能没有此约束,因此应采取所有措施替换:

// Goes to the heap
Integer i = 817598;

… 这样:

// Stays on the stack
int i = 817598;

使用数组时,情况会变得更糟:

// Three heap objects!
Integer[] i = { 1337, 424242 };

… 这样:

// One heap object.
int[] i = { 1337, 424242 };

带走

当您深入了解NOPE分支时 ,应该非常警惕使用包装器类型。 可能会给GC带来很大压力,必须时刻加油清理垃圾。

一种特别有用的优化可能是使用某种原始类型并为其创建大型的一维数组,以及几个定界符变量以指示编码对象在数组上的确切位置。

trove4j是一个出色的原始集合库,它比您的平均int[]要复杂一些 ,它随LGPL一起提供。

例外

此规则有一个例外: booleanbyte值很少,因此无法完全由JDK缓存。 你可以写:

Boolean a1 = true; // ... syntax sugar for:
Boolean a2 = Boolean.valueOf(true);Byte b1 = (byte) 123; // ... syntax sugar for:
Byte b2 = Byte.valueOf((byte) 123);

对于其他整数基本类型的低值(包括charshortintlong

但是仅当您将它们自动装箱或调用TheType.valueOf() ,才调用构造函数!

除非确实需要新实例,否则切勿在包装器类型上调用构造函数。

这个事实还可以帮助您为同事写一个复杂的,愚蠢的愚人节玩笑

堆外

当然,您可能还想尝试堆外数据库,尽管它们更多是一个战略决策,而不是本地优化。

彼得·劳瑞(Peter Lawrey)和本·科顿(Ben Cotton)撰写的有关该主题的有趣文章是: OpenJDK和HashMap…安全地教老狗新手(超堆!)技巧

6.避免递归

像Scala这样的现代函数式编程语言鼓励使用递归,因为它们提供了将尾递归算法优化回到迭代算法的方法 。 如果您的语言支持这种优化,则可能会很好。 但是即使这样,算法的最细微改动也可能会产生一个分支,从而阻止您的递归成为尾递归。 希望编译器能够检测到这一点! 否则,您可能会浪费大量的堆栈框架,而这些内容可能只使用了几个局部变量就已经实现了。

带走

除了以下内容外,没有什么要说的:当您深入NOPE分支时,始终喜欢迭代而不是递归。

7.使用entrySet()

如果要遍历Map ,并且既需要键需要值,则必须有一个很好的理由来编写以下内容:

for (K key : map.keySet()) {V value : map.get(key);
}

…而不是以下内容:

for (Entry<K, V> entry : map.entrySet()) {K key = entry.getKey();V value = entry.getValue();
}

当您位于NOPE分支时 ,无论如何,您应该警惕地图,因为很多O(1)地图访问操作仍然很多。 而且访问也不是免费的。 但是至少,如果您不能没有地图,请使用entrySet()进行迭代! 无论如何,都有Map.Entry实例,您只需要访问它即可。

带走

在地图迭代期间同时需要键和值时,请始终使用entrySet()

8.使用EnumSet或EnumMap

在某些情况下,映射中可能的键数是预先已知的,例如在使用配置映射时。 如果该数字相对较小,则应真正考虑使用EnumSetEnumMap ,而不是常规的HashSetHashMap 。 通过查看EnumMap.put()可以很容易地解释这一点:

private transient Object[] vals;public V put(K key, V value) {// ...int index = key.ordinal();vals[index] = maskNull(value);// ...
}

此实现的本质是这样一个事实,即我们拥有一个索引值数组而不是哈希表。 当插入一个新值时,我们要查找映射项的所有工作就是向枚举询问其常量序数,该序数由Java编译器在每种枚举类型上生成。 如果这是一个全局配置映射(即,仅一个实例),则提高的访问速度将帮助EnumMap大大优于HashMap ,后者可能使用较少的堆内存,但必须在每个键上运行hashCode()equals()

带走

EnumEnumMap是非常好的朋友。 每当您将类似枚举的结构用作键时,请考虑实际上使这些结构成为枚举并将其用作EnumMap键。

9.优化您的hashCode()和equals()方法

如果您不能使用EnumMap ,至少要优化您的hashCode()equals()方法。 一个好的hashCode()方法是必不可少的,因为它将阻止对更昂贵的equals()进一步调用,因为它将为每个实例集生成更多不同的哈希存储桶。

在每个类层次结构中,您可能都有流行和简单的对象。 让我们看一下jOOQ的org.jooq.Table实现。

hashCode()的最简单,最快的实现是:

// AbstractTable, a common Table base implementation:@Override
public int hashCode() {// [#1938] This is a much more efficient hashCode()// implementation compared to that of standard// QueryPartsreturn name.hashCode();
}

…其中name只是表名。 我们甚至不考虑表的模式或任何其他属性,因为表名通常在数据库中足够不同。 另外,该name是一个字符串,因此它内部已经有一个缓存的hashCode()值。

该注释很重要,因为AbstractTable扩展了AbstractQueryPart ,它是任何AST(抽象语法树)元素的通用基本实现。 通用AST元素没有任何属性,因此它不能做任何假设来优化hashCode()实现。 因此,重写的方法如下所示:

// AbstractQueryPart, a common AST element
// base implementation:@Override
public int hashCode() {// This is a working default implementation. // It should be overridden by concrete subclasses,// to improve performancereturn create().renderInlined(this).hashCode();
}

换句话说,必须触发整个SQL呈现工作流以计算公共AST元素的哈希码。

equals()事情变得更加有趣

// AbstractTable, a common Table base implementation:@Override
public boolean equals(Object that) {if (this == that) {return true;}// [#2144] Non-equality can be decided early, // without executing the rather expensive// implementation of AbstractQueryPart.equals()if (that instanceof AbstractTable) {if (StringUtils.equals(name, (((AbstractTable<?>) that).name))) {return super.equals(that);}return false;}return false;
}

第一件事: 始终 (不仅在NOPE分支中 )提前中止每个equals()方法,如果:

  • this == argument
  • this "incompatible type" argument

请注意,如果您使用instanceof检查兼容类型,则后一个条件包括argument == null 。 之前我们在“编码Java的10个微妙的最佳实践”中已经对此进行过博客撰写。

现在,在明显的情况下尽早中止比较之后,您可能还想在可以做出部分决策时就中止比较。 例如,jOOQ的Table.equals()Table.equals()是要使两个表相等,无论具体的实现类型如何,它们都必须具有相同的名称。 例如,这两个项目不可能相等:

  • com.example.generated.Tables.MY_TABLE
  • DSL.tableByName("MY_OTHER_TABLE")

如果argument 不能等于this ,并且如果我们可以轻松地进行检查,那么我们可以这样做,如果检查失败,则中止。 如果检查成功,我们仍然可以从super进行更昂贵的实现。 鉴于Universe中的大多数对象不相等,我们将通过简化此方法来节省大量CPU时间。

有些对象比其他对象更平等

在jOOQ的情况下,大多数实例实际上是由jOOQ源代码生成器生成的表,该表的equals()实现甚至得到了进一步优化。 其他数十种表类型(派生表,表值函数,数组表,联接表,数据透视表,公用表表达式等)可以保持其“简单”实现。

10.集合思考,而不是个别思考

最后但并非最不重要的一点是,它与Java不相关,但适用于任何语言。 此外,我们将离开NOPE分支,因为此建议可能只是帮助您从O(N 3 )迁移到O(n log n)或类似的东西。

不幸的是,许多程序员认为是简单的本地算法。 他们一步一步地解决问题,逐分支,逐循环,逐方法。 那就是命令式和/或函数式编程风格。 从纯粹的命令式到面向对象(仍然是命令式)再到函数式编程时,为“更大的画面”建模变得越来越容易,但是所有这些样式都缺少只有SQL和R和类似语言才能具备的功能:

声明式编程。

在SQL中( 并且我们很喜欢,因为它是jOOQ博客 ),您可以声明要从数据库中获取的结果,而不会产生任何算法含义。 然后,数据库可以考虑所有可用的元数据( 例如约束,键,索引等 ),以找出可能的最佳算法。

从理论上讲,从一开始,这就是SQL和关系演算背后的主要思想。 实际上,SQL供应商仅在最近十年才实施了高效的CBO(基于成本的优化工具) ,因此请与我们保持在一起,直到2010年SQL最终释放出其全部潜力(大约是时间!)。

但是,您不必执行SQL即可进行集合思考。 集合/收藏/袋子/清单可提供所有语言和库。 使用集合的主要优点是您的算法将变得更加简洁。 编写起来非常容易:

SomeSet INTERSECT SomeOtherSet

而不是:

// Pre-Java 8
Set result = new HashSet();
for (Object candidate : someSet)if (someOtherSet.contains(candidate))result.add(candidate);// Even Java 8 doesn't really help
someSet.stream().filter(someOtherSet::contains).collect(Collectors.toSet());

有人可能会认为函数式编程和Java 8将帮助您编写更简单,更简洁的算法。 不一定是真的。 您可以将命令性的Java-7循环转换为功能性的Java-8 Stream集合,但是您仍在编写完全相同的算法。 编写类似SQL的表达式是不同的。 这个…

SomeSet INTERSECT SomeOtherSet

…可以由实施引擎以1000种方式实施。 正如我们今天所了解的那样,在运行INTERSECT操作之前将两个集合自动转换为EnumSet也许是明智的。 也许我们可以并行化此INTERSECT而无需对Stream.parallel()进行低级调用。

结论

在本文中,我们讨论了在NOPE分支上进行的优化,即深入到高复杂度算法中。 在我们的案例中,作为jOOQ开发人员,我们有兴趣优化我们SQL生成:

  • 每个查询仅在单个StringBuilder上生成
  • 我们的模板引擎实际上是解析字符,而不是使用正则表达式
  • 我们会尽可能使用数组,尤其是在侦听器上进行迭代时
  • 我们避免了不必调用的JDBC方法
  • 等等…

jOOQ处于“食物链的底部”,因为它是(次)API,在调用离开JVM进入DBMS之前,我们的客户应用程序正在调用它。 位于食物链的底部意味着在jOOQ中执行的每一行代码都可能被称为N x O x P倍,因此我们必须热切地进行优化。

您的业​​务逻辑不在NOPE分支中 。 但是您自己的本地基础结构逻辑可能是(自定义SQL框架,自定义库等),应该根据我们今天所看到的规则进行审查。 例如,使用Java Mission Control或任何其他探查器。

翻译自: https://www.javacodegeeks.com/2015/02/top-10-easy-performance-optimisations-java.html

java 性能 优化

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/338067.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

cx_oracle写日志信息_看日志痛苦——可能是你方法不对

看日志&#xff0c;是研发的日常工作&#xff0c;相信你已经从各种报错日志中查出许多BUG&#xff0c;但也被冗长、复杂的日志困扰过无数次。在日常工作中&#xff0c;我也经常痛苦于看日志&#xff0c;一般是这些日志长得面目狰狞&#xff1a;日志文件太大&#xff0c;打开都很…

在Swift中使用C语言的指针

点击上方蓝字关注我&#xff0c;了解更多咨询Objective-C和C语言经常需要使用到指针。Swift中的数据类型由于良好的设计&#xff0c;使其可以和基于指针的C语言API无缝混用。同时 Swift也可以自动处理大多数将指针作为参数的情况。在这篇文章里&#xff0c;我们可以看到在Swift…

android 音频合成_【Android工具】用手机测量噪声的工具软件,噪声仪分贝计,量化噪声声音工具...

今天分享一个通过手机麦克风测量环境噪声的工具——声级计(噪声仪)。本来是要分享另一款的&#xff0c;但下载下来的是xapk的安装包&#xff0c;太麻烦了&#xff0c;功能差不多&#xff0c;大家就先用这款吧&#xff0c;有条件的朋友可以去play自己下载。软件名称&#xff1a;…

cassandra_Apache Cassandra和低延迟应用程序

cassandra介绍 多年来&#xff0c; Grid Dynamics拥有许多与NoSQL相关的项目&#xff0c;尤其是Apache Cassandra。 在这篇文章中&#xff0c;我们要讨论一个给我们带来挑战的项目&#xff0c;而我们在该项目中试图回答的问题今天也仍然适用。 数字营销和在线广告在2012年很受…

浅析五种C语言内存分配的方法及区别

点击上方蓝字关注我&#xff0c;了解更多咨询在C语言中&#xff0c;内存分成5个区&#xff0c;他们分别是堆、栈、自由存储区、全局/静态存储区和常量存储区。栈&#xff0c;就是那些由编译器在需要的时候分配&#xff0c;在不需要的时候自动清楚的变量的存储区。里面的变量通常…

电脑装机完没有efi_电脑装机如何选内存?看完这篇就全懂了

电脑运行太慢&#xff0c;很多小伙伴第一想到的就是加个内存&#xff0c;没错&#xff0c;一般主板上预留了两个以上的内存插槽&#xff0c;加内存是升级电脑最划算的一个硬件升级&#xff0c;其次&#xff0c;可能是加个固态硬盘来提速。今天电脑学习小编教你如何选择适合自己…

塞尔达盾反机器人_微软商店惊现《塞尔达传说:旷野之息》!任天堂暗示《喷射战士3》?| Jump简报...

首先还是祝各位Jumper圣诞快乐&#xff01;业界新闻1. 各大厂商新年寄语&#xff0c;表明未来动向Fami通收到了54家日厂的新年贺卡&#xff0c;其中表明了相当多的新游戏和企划&#xff0c;具体如下&#xff1a;Atlus &#xff1a;《真女神转生V》和《PROJECT Re FANTASY》正在…

PHP 社区拒绝在俄乌冲突中“站队”

点击上方蓝字关注我&#xff0c;了解更多咨询几天前&#xff0c;PHP 社区邮件讨论列表出现了一封“申请援助乌克兰”的邮件&#xff0c;该邮件由非 PHP 开发者发起&#xff0c;目的在于呼吁 PHP 社区火速参与俄乌冲突...邮件机翻译文如下&#xff1a;有意思的是&#xff0c;非 …

jvm内存 大于 xmx_为什么我的JVM访问的内存少于通过-Xmx指定的内存?

jvm内存 大于 xmx“嘿&#xff0c;你能来看看奇怪的东西吗&#xff1f;” 这就是我开始研究支持案例的方式&#xff0c;将我引向了这篇博客文章。 眼前的具体问题与报告可用内存数量不同的不同工具有关。 简而言之&#xff0c;一位工程师正在研究特定应用程序的过多内存使用情…

为什么说PHP是很糟糕的,也是很好的编程语言

点击上方蓝字关注我&#xff0c;了解更多咨询PHP 又是一门相当奇怪的编程语言。当人们抱怨这门语言“很糟糕”时&#xff0c;他们并没有说错。这门语言确实有很多不好的地方。搁在以前&#xff0c;这门语言还有更多糟糕的问题。嘲笑 PHP 的博文《全面解析 PHP 的糟糕设计》(PHP…

Objective-C学习中对 C语言的扩展

点击上方蓝字关注我&#xff0c;了解更多咨询Objective-C学习中对 C 的扩展是本文要介绍的内容&#xff0c;Objective-C和Cocoa是苹果公司Mac OS X操作系统的核心。Objective-C语言是C语言的一个扩展集&#xff0c;许多具备Mac OS X外观的应用程序都是使用该语言开发的。Cocoa是…

神武4手游服务器维护,神武4手游 本周新手服限服开启 !

《神武4》手游新老玩家互动福利新手服即将于本周在天下无双、二〇二〇、见龙在田限服开启&#xff0c;通过“老带新”模式&#xff0c;助力萌新玩家快乐成长的同时&#xff0c;也为老玩家送出更多福利好礼。【《神武4》手游新手服限服开启 】服务器等级≥65且自身等级≥69级的玩…

双向数据绑定是什么

一、什么是双向绑定 我们先从单向绑定切入单向绑定非常简单&#xff0c;就是把Model绑定到View&#xff0c;当我们用JavaScript代码更新Model时&#xff0c;View就会自动更新双向绑定就很容易联想到了&#xff0c;在单向绑定的基础上&#xff0c;用户更新了View&#xff0c;Mo…

织梦网站上传服务器不显示图片,解决织梦后台登陆不显示验证码图片问题

最近在工作中遇到一个问题&#xff0c;用织梦搭建好的网站&#xff0c;在本地上测试没问题但是上传到正式服务器上就出问题了&#xff0c;在后台登陆的时候&#xff0c;验证码的图片老是显示不出来&#xff0c;后来查阅了相关资料才终于找到问题的根本原因&#xff0c;下面就分…

Python与C语言的区别是什么?

点击上方蓝字关注我&#xff0c;了解更多咨询Python与C语言的区别是什么?Python是由C语言实现&#xff0c;C语言是编译型语言&#xff0c;经过编译后生成机器码再运行&#xff0c;执行速度快不能跨平台&#xff0c;一般用于操作系统驱动等底层开发。Python是理解为解释型语言执…

C语言数据类型从计算机原理的角度是怎样看待的?

点击上方蓝字关注我&#xff0c;了解更多咨询初学C语言&#xff0c;首先要接触的就是数据类型了&#xff0c;这也是学习任何一门语言所必须经历的阶段。很多同学在学习的时候不理解数据类型&#xff0c;因为对计算机及原理知之甚少。所以&#xff0c;在学习数据类型之前&#x…

android module中获取 app_Android组件化架构 - 4. 动态创建

Android 组件化中使用动态创建的作用是解耦&#xff1b;1. 反射机制反射有两个作用&#xff1a;1.反编译&#xff1a;.class->.java;2.通过反射机制访问java对象中的属性&#xff0c;方法&#xff0c;构造器等&#xff1b;实现反射&#xff0c;实际上是得到Class对象2. 动态…

小白适用的C语言数据类型转换及转换规则

点击上方蓝字关注我&#xff0c;了解更多咨询1.不同类型数据间的混合运算与类型转换&#xff1a;①若参与运算量的类型不同&#xff0c;则先转换成同一类型&#xff0c;然后进行运算②转换按数据长度增加的方向进行&#xff0c;以保证精度不降低。如int型和long型运算时&#x…

jax-ws和jax-rs_带有JAX-RS和PrimeFaces的RESTful图表

jax-ws和jax-rs通常&#xff0c;利用图表提供数据的直观表示很有用。 PrimeFaces提供制图解决方案&#xff0c;可轻松将数据的可视表示形式添加到Web和移动应用程序中。 如果我们将PrimeFaces图表组件的使用与RESTful Web服务数据结合在一起&#xff0c;我们可以创建自定义图表…

udp 使用connect优点_nodejs源码分析第十九章 -- udp模块

udp不是面向连接的协议&#xff0c;所以使用上会比tcp简单&#xff0c;他和tcp一样&#xff0c;使用四元组来标记通信的双方&#xff08;单播的情况下&#xff09;。我们看看udp作为服务器和客户端的时候的流程。1 在c语言中使用udp1.1 服务器流程&#xff08;伪代码&#xff0…